为什么80%的码农都做不了架构师?>>>   

这篇是承接《轻量级 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 的插件来实现这一目标:

@Plugin
public 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” 包下。

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

@Plugin
public 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 赵永春(zyc@hasor.net)*/
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 的相关代码。

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

Guice Aop 与 Hasor Aop 原理及其实现相关推荐

  1. AOP—JVM SandBox—底层原理解析

    原文作者:陆晨 原文地址:JVM SandBox 的技术原理与应用分析 目录 一.前言 二.JVM SandBox 简介 2.1 AOP 2.2 JVM SandBox 三.JVM 核心技术 3.1 ...

  2. 底层实现_Java AOP的底层实现原理

    AOP用于处理系统中分布于各个模块的横切关注点,比如事务管理.日志.缓存等. AOP实现的关键,在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ ...

  3. 一文搞懂Spring AOP源码底层原理

    一.什么是AOP 与OOP对比,AOP是处理一些横切性问题,这些横切性问题不会影响到主逻辑实现的,但是会散落到代码的各个部分,难以维护.一键获取源码地址spring aop面试题 AOP就是把这些问题 ...

  4. PHP之AOP面向切面编程原理(2)

    在传统的编写业务逻辑处理代码时,我们通常会习惯性地做几件事情:日志记录.事务控制及权限控制等,然后才是编写核心的业务逻辑处理代码.当代码编写完成回头再看时,不禁发现,扬扬洒洒上百行代码中,真正用于核心 ...

  5. 15-Spring AOP的底层实现原理JDKProxyCGLIB

    前一篇:14-Spring AOP编程https://blog.csdn.net/fsjwin/article/details/109480097 前面我们讨论动静态理的时候说过,想要实现动态代理需要 ...

  6. AOP和Spring AOP介绍

    AOP和Spring AOP介绍 文章目录 AOP和Spring AOP介绍 一.AOP简介 二. 传统开发存在的弊端 三. AOP实现原理 四.Spring AOP 五.AOP相关术语 一.AOP简 ...

  7. aop中joinpoint_Spring AOP示例教程–方面,建议,切入点,JoinPoint,注释,XML配置...

    aop中joinpoint Spring Framework is developed on two core concepts – Dependency Injection and Aspect O ...

  8. spring AOP中 aop:advisor 与 aop:aspect 的区别

    在开发过程中,不少有Spring Aop的使用, 在面向切面编程时,我们会使用< aop:aspect>: 在进行事务管理时,我们会使用< aop:advisor>. 那么,对 ...

  9. aopaspect区别_面试官:什么是AOP?Spring AOP和AspectJ的区别是什么?

    AOP(Aspect Orient Programming),它是面向对象编程的一种补充,主要应用于处理一些具有横切性质的系统级服务,如日志收集.事务管理.安全检查.缓存.对象池管理等. AOP实现的 ...

最新文章

  1. C++编程基础二 04-默认实参
  2. matlab有限差分一维导热,一维导热方程-有限差分法-matlab实现11.docx
  3. 面试问烂的 MySQL 四种隔离级别,看完吊打面试官!
  4. PyQt4基本布局常用方法之addSpacing
  5. 进程同步算法实现实验报告Linux,操作系统进程同步实验报告.doc
  6. Android官方开发文档Training系列课程中文版:与其它APP交互之将用户带到其它的APP
  7. C/C++常量数据类型
  8. C#实现HttpUtility.UrlEncode输出大写字母
  9. 研发解决方案介绍#Tracing(鹰眼)
  10. Kaldi AMI数据集脚本学习6---转移模型(Transition Model)
  11. js手机键盘遮挡_移动端页面input输入框被键盘遮挡问题
  12. 深度学习CNN, R-CNN
  13. 浅谈Event Loop
  14. SitePoint播客#115:直播在WordCamp Raleigh第2部分
  15. ModbusTcp协议详解
  16. 二进制转8421bcd码_绝对值编码器当中的格雷码
  17. java native 方法和修饰符
  18. 软件研发的6sigma案例解析
  19. Centos7安装gos脚本
  20. 暗影精灵5风扇怎么调_惠普暗影精灵5内部结构是怎么样的 他的散热效果好不好呢...

热门文章

  1. h5跳转小程序页面url_微信小程序页面跳转方法
  2. MySQL懒查询_mysql 联查的基本命令
  3. 基础html的网页,[网页设计]HTML基础(五)——
  4. c语言自定义函数案例情景,第4周 C语言及程序设计提高例程-3 体验自定义函数...
  5. cas修改界面html,为REST API配置的CAS将v1 / ticket重定向到登录HTML
  6. POJ - 3461 (kmp)
  7. MySQL修改主键初始值为1
  8. matlab无穷积分求解_python做微积分
  9. php中ini set,php ini_set函数的用法
  10. python写一个计时器_Python 实现一个计时器