博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Guice Aop 与 Hasor Aop 原理及其实现
阅读量:5981 次
发布时间:2019-06-20

本文共 9684 字,大约阅读时间需要 32 分钟。

  hot3.png

    这篇是承接《》系列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
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();    }}

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

    使用 Hasor 的 Aop 除了简单的通过 注解就可以实现之外,拦截器类还可以被依赖注入。这样一来 Guice 在 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 配置, 并不支持全局配置。因此需要借助 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 中 并不是 Hasor 核心直接提供的功能。它是由一个 Hasor 插件提供的,这个插件仅有几个类组成。它们位于:“net.hasor.plugins.aop” 包下。

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

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

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

/** * 负责检测匹配。规则:只要类型或方法上标记了某个注解。 * @version : 2013-11-22 * @author 赵永春(zyc@hasor.net) */class MatcherAnnotationType extends AbstractMatcher {    private Class
annotationType = null; public MatcherAnnotationType(Class
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
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
interTypeList, MethodInvocation invocation) { List
beforeList = new ArrayList
(); for (Class
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:    
    git@OSC: 

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

转载于:https://my.oschina.net/ta8210/blog/178369

你可能感兴趣的文章
Java小细节
查看>>
洛谷 P2486 BZOJ 2243 [SDOI2011]染色
查看>>
数值积分中的辛普森方法及其误差估计
查看>>
Web service (一) 原理和项目开发实战
查看>>
跑带宽度多少合适_跑步机选购跑带要多宽,你的身体早就告诉你了
查看>>
Javascript异步数据的同步处理方法
查看>>
iis6 zencart1.39 伪静态规则
查看>>
SQL Server代理(3/12):代理警报和操作员
查看>>
Linux备份ifcfg-eth0文件导致的网络故障问题
查看>>
2018年尾总结——稳中成长
查看>>
通过jsp请求Servlet来操作HBASE
查看>>
Shell编程基础
查看>>
Shell之Sed常用用法
查看>>
Centos下基于Hadoop安装Spark(分布式)
查看>>
mysql开启binlog
查看>>
设置Eclipse编码方式
查看>>
分布式系统唯一ID生成方案汇总【转】
查看>>
并查集hdu1232
查看>>
Mysql 监视工具
查看>>
Linux Namespace系列(09):利用Namespace创建一个简单可用的容器
查看>>