一文带你搞懂从动态代理实现到 Spring AOP
摘要:本文主要讲了 Spring Aop 动态代理实现的两种方式。
1. Spring AOP
Spring 是一个轻型容器,Spring 整个系列的最最核心的概念当属 IoC、AOP。可见 AOP 是 Spring 框架中的核心之一,在应用中具有非常重要的作用,也是 Spring 其他组件的基础。AOP(Aspect Oriented Programming),即面向切面编程,可以说是 OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP 引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过 OOP 允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。
关于 AOP 的基础知识,并不是本文的重点,我们主要来看下 AOP 的核心功能的底层实现机制:动态代理的实现原理。AOP 的拦截功能是由 java 中的动态代理来实现的。在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的 Interceptor 的种类,如 BeforeAdviseInterceptor,AfterAdviseInterceptor 以及 ThrowsAdviseInterceptor 等)。
那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现 AOP 中用到的两种动态代理。
AOP 的源码中用到了两种动态代理来实现拦截切入功能:jdk 动态代理和 cglib 动态代理。两种方法同时存在,各有优劣。jdk 动态代理是由 java 内部的反射机制来实现的,cglib 动态代理底层则是借助 asm 来实现的。总的来说,反射机制在生成类的过程中比较高效,而 asm 在生成类之后的相关执行过程中比较高效(可以通过将 asm 生成的类进行缓存,这样解决 asm 生成类过程低效问题)。
下面我们分别来示例实现这两种方法。
2. JDK 动态代理
2.1 定义接口与实现类
上面代码定义了一个被拦截对象接口,即横切关注点。下面代码实现被拦截对象接口。
2.2 JDK 动态代理类
上述代码实现了动态代理类 JDKProxy,实现 InvocationHandler 接口,并且实现接口中的 invoke 方法。当客户端调用代理对象的业务方法时,代理对象执行 invoke 方法,invoke 方法把调用委派给 targetObject,相当于调用目标对象的方法,在 invoke 方法委派前判断权限,实现方法的拦截。
2.3 测试
结果如下:
3. CGLIB 字节码生成
3.1 要代理的类
CGLIB 既可以对接口的类生成代理,也可以针对类生成代理。示例中,实现对类的代理。
该类的实现和上面的接口实现一样,为了保持统一。
3.2 CGLIB 动态代理类
上述实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,扩展父类的 class 来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj 表示目标类的实例,method 为目标类方法的反射对象,args 为方法的动态入参,methodProxy 为代理类实例。method.invoke(targetObject, args)通过代理类调用父类中的方法。
3.3 测试
结果如下:
4. 总结
本文主要讲了 Spring Aop 动态代理实现的两种方式,并分别介绍了其优缺点。jdk 动态代理的应用前提是目标类基于统一的接口。如果没有该前提,jdk 动态代理不能应用。由此可以看出,jdk 动态代理有一定的局限性,cglib 这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
JDK 动态代理机制是委托机制,不需要以来第三方的库,只要要 JDK 环境就可以进行代理,动态实现接口类,在动态生成的实现类里面委托为 hanlder 去调用原始实现类方法;CGLib 必须依赖于 CGLib 的类库,使用的是继承机制,是被代理类和代理类继承的关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。
参考
jdk 动态代理代理与 cglib 代理原理探究
本文分享自华为云社区《还不懂 Spring AOP?一文带你搞懂动态代理》,原文作者:aoho 。
版权声明: 本文为 InfoQ 作者【华为云开发者社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/891d25dd21ee546961ad62854】。文章转载请联系作者。
评论