侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

Guice Aop 与 Hasor Aop 原理及其实现

2022-06-28 星期二 / 0 评论 / 0 点赞 / 64 阅读 / 15142 字

这篇是承接《轻量级 Java 开发框架 设计》系列Blog文的后续文章,本文主要介绍 Hasor 中 AOP 方面的设计以及实现。 提起 Aop 大家并不陌生,OSC中也不缺乏优秀的介绍文章。

    这篇是承接《轻量级 Java 开发框架 设计》系列Blog文的后续文章,本文主要介绍 Hasor 中 AOP 方面的设计以及实现。

    提起 Aop 大家并不陌生,OSC中也不缺乏优秀的介绍文章。因而我也不费唇舌去介绍什么是 AOP 以及AOP 的切面概念。下面就转入正题。

    Hasor 的核心采用的是 Google Guice 也就是说,AOP 核心实现也就是 Guice 提供的。因此本文首先要介绍 Guice 是如何实现 Aop 然后在介绍 Hasor 的 Aop 实现方式。所以什么 CGLib、ASM、Javassist 都先闪到一遍去把,没必要搞一个重复的功能添加进来。

    本文将分为三个部分讲解:
1 Guice Aop:介绍使用原生 Guice 方法实现 Aop。
2 Hasor Aop:介绍使用 Hasor 的 Aop 如何使用。
3 讲解 Hasor 是如何实现 Aop 以及如何处理 Aop 链问题的。

Guice Aop

    先看看如何使用原生 Guice 实现 AOP 把,下面是 Guice 中一个标准的切面完整代码:

class MyInterceptor implements MethodInterceptor {    public Object invoke(MethodInvocation invocation) throws Throwable {        System.out.println("before.");        Object returnData = invocation.proceed();        System.out.println("after.");        return returnData;    }}

    创建 Guice 使用下面这个代码:

class MyModule implements Module {    public void configure(Binder binder) {        // TODO Auto-generated method stub    }}public static void main(String[] args) {    Guice.createInjector(new MyModule());}

    下面我们将介绍如何确定哪些类需要 Aop。首先 Guice 是通过两个匹配器完成 Aop 是否加装的判断。 它们一个是负责匹配类、一个是负责匹配类中的方法。

    使用 Guice 做 Aop 有一个好处就是,你永远不需要预先准备哪些类需要 Aop。当你通过 Guice 创建类对象时 Guice 会通过匹配器来判断创建的类型是否满足加装 Aop,以及加装哪些 Aop。

    接下来我们会关心匹配器的问题,下面这段代码中 “Matchers.inSubpackage("org.more.test")” 表示匹配“org.more.test.guice” 包下的所有类。而 “Matchers.any()” 部分的含义是匹配所有方法。

public class AopTest {    static class MyInterceptor implements MethodInterceptor {        public Object invoke(MethodInvocation invocation) throws Throwable {            System.out.println("before.");            Object returnData = invocation.proceed();            System.out.println("after.");            return returnData;        }    }    static class MyModule implements Module {        public void configure(Binder binder) {            Matcher<Class> m = Matchers.inSubpackage("org.more.test");            binder.bindInterceptor(m, Matchers.any(), new MyInterceptor());        }    }    //    public void foo() {        System.out.println("Hello Word.");    }    //    public static void main(String[] args) {        Injector injector = Guice.createInjector(new MyModule());        AopTest obj = injector.getInstance(AopTest.class);        obj.foo();    }}

    如果你对“Matchers.inSubpackage("org.more.test")”这种方式感到不满,可以通过实现 Matcher 接口制定适合自己的筛选器。Hasor 就是通过制定筛选器达到目的的。

    好了上面对 Guice 本身如何实现 Aop 做了一个简短的介绍,接下来将介绍 Hasor 中如何实现 Aop 的。

Hasor Aop

    从理论上来说 Aop 被分为三个切面(调用前、调用后、异常发生)。同时当配置多个 Aop 时还会涉及到 “链” 的问题。而这就像是同心圆环,最内层的是目标方法。要想调用到目标方法需要逐一经过其外面的 Aop切面程序。

    在实现 Aop 时候有两条路可以选择:

    一、是制定一个类似 Filter 的接口 Api 使调用链式结构的 Aop代理变得像是在操作过滤器。
    二、是声明定义三个接口专门用于通知切面程序三个切点事件,在拦截器中切点位置执行切面方法调用。

    前一种方式可能会面临设计结构的问题,这种方式可以在“Aop过滤器”链中控制是否要继续向下执行。而后一种虽然结构非常清晰,但是切面程序也失去了对 Aop 链控制。

    从开发角度来看 “过滤器” 也可以得到(调用前、调用后、异常发生)这三个事件点。因此 Hasor 放弃了第二种实现方式。那么先看一下使用 Hasor Api 如何开发 Aop 程序:

public class SimpleAop_Test {    public static class MyInterceptor implements MethodInterceptor {        public Object invoke(MethodInvocation invocation) throws Throwable {            System.out.println("before.");            Object returnData = invocation.proceed();            System.out.println("after.");            return returnData;        }    }    //    @Aop(MyInterceptor.class)    public void foo() {        System.out.println("Hello Word.");    }    //    public static void main(String[] args) throws IOException {        AppContext appContext = new AnnoStandardAppContext();        appContext.start();        //        SimpleAop_Test obj = appContext.getInstance(SimpleAop_Test.class);        obj.foo();    }}

    从代码比较上来看,除了使用 @Aop 注解并没有什么太大的改进。不过在这里我要先给大家说明一下,Guice 是一款 IoC/Aop 框架。切面程序按理来说一旦被容器管理也应当可以被依赖注入。不过您可以留心观察一下,在 Guice 的原生实现方式中拦截器是被 new 出来的,通过查看 Guice API也不难发现注册拦截器也只有这一种方式。

    使用 Hasor 的 Aop 除了简单的通过 @Aop 注解就可以实现之外,拦截器类还可以被依赖注入。这样一来 Guice 在 Aop 开发方面就变得更加友好了。

    @Aop 注解是可以被标记到(方法 或 类)上,通过标记位置的不同决定了 Aop 作用范围。当然您可以混合使用例如:

@Aop(MyInterceptor.class)public class SimpleAop_Test {    public void foo1() {        System.out.println("Hello Word.");    }    @Aop(MyInterceptor2.class)    public void foo2() {        System.out.println("Hello Word.");    }}

    或许有的时候还需要全局 Aop 配置,@Aop 并不支持全局配置。因此需要借助 Hasor 的插件来实现这一目标:

@Pluginpublic class GlobalAopInterceptor implements HasorPlugin {    public void loadPlugin(ApiBinder apiBinder) {        Matcher matcher = ......;        apiBinder.getGuiceBinder().bindInterceptor(//                matcher, Matchers.any(), new MyInterceptor());    }}

Hasor Aop 实现源码分析

    那么 Hasor 是如何实现这一功能的呢?

    在 Hasor 中 @Aop 并不是 Hasor 核心直接提供的功能。它是由一个 Hasor 插件提供的,这个插件仅有几个类组成。它们位于:“net.hasor.plugins.aop” 包下。

    接下来让我们我们先看看插件入口程序:

@Pluginpublic class AopPlugin extends AbstractHasorPlugin {    public void loadPlugin(ApiBinder apiBinder) {        Matcher<Object> matcher = AopMatchers.annotatedWith(Aop.class);//        apiBinder.getGuiceBinder().bindInterceptor(matcher, matcher, new AopInterceptor());    }}

    作为 Aop 实现的入口我们看到,Aop 插件仅仅是向 Guice 注册了一个拦截器。这个拦截器负责拦截所有标记了 @Aop 注解的类或方法。下面是相关匹配器的代码:

/** * 负责检测匹配。规则:只要类型或方法上标记了某个注解。 * @version : 2013-11-22 * @author 赵永春([email protected]) */class MatcherAnnotationType extends AbstractMatcher<Object> {    private Class<? extends Annotation> annotationType = null;    public MatcherAnnotationType(Class<? extends Annotation> annotationType) {        this.annotationType = annotationType;    }    public boolean matches(Object type) {        if (type instanceof Class<?>)            return this.matches((Class<?>) type);        if (type instanceof Method)            return this.matches((Method) type);        return false;    }    public boolean matches(Class<?> matcherType) {        if (matcherType.isAnnotationPresent(this.annotationType) == true)            return true;        Method[] m1s = matcherType.getMethods();        Method[] m2s = matcherType.getDeclaredMethods();        for (Method m1 : m1s) {            if (m1.isAnnotationPresent(this.annotationType) == true)                return true;        }        for (Method m2 : m2s) {            if (m2.isAnnotationPresent(this.annotationType) == true)                return true;        }        return false;    }    public boolean matches(Method matcherType) {        if (matcherType.isAnnotationPresent(this.annotationType) == true)            return true;        if (matcherType.getDeclaringClass().isAnnotationPresent(this.annotationType) == true)            return true;        return false;    }}

    有了这个匹配器,只要调用带有 @Aop 标记的类或方法。都会进入我们的拦截器 AopInterceptor,下面是拦截器代码(为了缩短代码长度,下面的代码中去掉了部分泛型声明):

class AopInterceptor implements MethodInterceptor, AppContextAware {    private AppContext appContext           = null;    private Map        methodInterceptorMap = new HashMap();    //    public AopInterceptor() {        AwareUtil.registerAppContextAware(this);    }    //    public void setAppContext(AppContext appContext) {        this.appContext = appContext;    }    //    public Object invoke(MethodInvocation invocation) throws Throwable {       Method targetMethod = invocation.getMethod();       List<Class> list = this.methodInterceptorMap.get(targetMethod);       //1.取得拦截器       if (list == null) {         list = new ArrayList();         Aop beforeAnno = targetMethod.getDeclaringClass().getAnnotation(Aop.class);         if (beforeAnno != null) {             for (Class interType : beforeAnno.value())                 if (interType != null)                     list.add(interType);         }         beforeAnno = targetMethod.getAnnotation(Aop.class);         if (beforeAnno != null) {             for (Class interType : beforeAnno.value())                 if (interType != null)                     list.add(interType);         }         this.methodInterceptorMap.put(targetMethod, list);        }        //2.创建对象        return new AopChainInvocation(appContext, list, invocation).invoke(invocation);    }}

    上面这段代码中,有关“AppContextAware”部分的内容稍后介绍。首先我们假设 AppContext 已经存在。当拦截器拦截到符合 @Aop 的方法调用之后。这个拦截器会取得调用方法的 Method 对象。

    接下来拦截器会尝试从 Method 对象中获取 @Aop 注解中配置的拦截器信息。

    当然,这里考虑到了拦截器链的问题,因此会有一个 List 对象用于收集这个方法调用都配置了哪些真正的Aop 拦截器。

    最后利用收集到的信息构造一个“AopChainInvocation” 对象来处理调用过滤器链,下面是源码:

class AopChainInvocation implements MethodInvocation {    private MethodInterceptor[] beforeInterceptor = null;    private MethodInvocation    invocation        = null;    private int                 index             = -1;    //    public AopChainInvocation(AppContext appContext, List<Class> interTypeList, MethodInvocation invocation) {        List<MethodInterceptor> beforeList = new ArrayList<MethodInterceptor>();        for (Class<? extends MethodInterceptor> interType : interTypeList) {            if (interType != null)                beforeList.add(appContext.getInstance(interType));        }        this.beforeInterceptor = beforeList.toArray(new MethodInterceptor[beforeList.size()]);        this.invocation = invocation;    }    public Object invoke(MethodInvocation invocation) throws Throwable {        index++;        if (index < beforeInterceptor.length) {            return beforeInterceptor[index].invoke(this);        } else {            return invocation.proceed();        }    }    //-----------------------------------------------------------    public Object[] getArguments() {        return invocation.getArguments();    }    public Object proceed() throws Throwable {        return this.invoke(this.invocation);    }    public Object getThis() {        return invocation.getThis();    }    public AccessibleObject getStaticPart() {        return invocation.getStaticPart();    }    public Method getMethod() {        return invocation.getMethod();    }}

    AppContextAware接口是由“net.hasor.plugins.aware”插件提供的。这个插件功能是给予那些不方便获通过注入方式获取 AppContext 接口对象的类。在 AppContext 启动的第一时间给予它们注入。

    以上就是 Hasor 中有关 Aop 方面的完整说明。

----------------------------------------------------------------
目前的开发代码存放于(包括Demo程序)
    Github:    https://github.com/zycgit/hasor
    git@OSC: http://git.oschina.net/zycgit/hasor

非常感谢您百忙之中抽出时间来看这一系博文。可以通过Maven 中央仓库网站  http://search.maven.org/ 搜索 Hasor 下载 hasor 的相关代码。

广告 广告

评论区