上一章节分析了静态代理和JDK、CGLIB动态代理,接下来我们还要回顾一下AOP的一些相关知识,以方便为接下来分析AOP的源码做好准备。
1.先来回顾一下AOP中的一些术语。
- 连接点(Jointpoint):表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,Spring只支持方法执行连接点,在AOP中表示为“在哪里干”;
- 切入点(Pointcut):选择一组相关连接点的模式,即可以认为连接点的集合,Spring支持perl5正则表达式和AspectJ切入点模式,Spring默认使用AspectJ语法,在AOP中表示为“在哪里干的集合”;
- 增强(Advice):很多地方理解为通知,但是理解为增强更为准确,增强表示在连接点上执行的行为,增强提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置增强(before advice)、后置增强(after advice)、环绕增强(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以环绕连接点的拦截器链织入增强;在AOP中表示为“干什么”;
- 方面/切面(Aspect):横切关注点的模块化,比如上边提到的日志组件。可以认为是增强、引入和切入点的组合;在Spring中可以使用Schema和@AspectJ方式进行组织实现;在AOP中表示为“在哪干和干什么集合”;
- 引介增强(inter-type declaration):引介增强是一个比较特殊的增强,它不是在目标方法周围织入增强,而是为目标类创建新的方法或属性,所以引介增强的连接点是类级别的,而非方法级别的,Spring允许引入新的接口(必须对应一个实现)到所有被代理对象(目标对象), 在AOP中表示为“干什么(引入什么)”;
- 目标对象(Target Object):需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被增强的对象,从而也可称为“被增强对象”;由于Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象,在AOP中表示为“对谁干”;
- AOP代理(AOP Proxy):AOP框架使用代理模式创建的对象,从而实现在连接点处插入增强(即应用切面),就是通过代理来对目标对象应用切面。在Spring中,AOP代理可以用JDK动态代理或CGLIB代理实现,而通过拦截器模型应用切面。
- 织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期、类装载期、运行期进行。
上面所提到的概念,比较抽象,也比较枯燥,而且在实际的开发中使用切面编程只占很少一部分,但是如果大家对上面的概念无所了解的话,那么对接下来的源码分析必然是一头雾水,下面我们再通过几个例子,让大家对上面的概念有所了解,为源码分析做好准备。
2. 增强方式简介
也有很多人将增强理解为通知,但是理解为增强会更加准确,因为它表示在连接点上执行的行为,这个行为是目标类类所没有的,是为目标类增加了额外的方法或者其他的一些功能,所以理解为增强比通知更加贴切,当然如果有的人已经习惯了通知这个概念的话也无所谓,只要知道本文将的增强即是通知即可。
增强的类型有前置增强、后置返回增强、异常增强、环绕增强、引介增强、后置最终增强等。下面我们通过实例的例子来介绍一下,为了让大家能够更为深刻的理解SpringAop,我们不会一开始就讲解基于@AspectJ
或者基于Schema配置文件
的方式,而是从最基础开始讲解,毕竟AOP在实际开发中并不占太大的比重,相信很多人并没有深刻的理解。
接下来我们先讲解基于Advice接口以编码方式实现的增强。
3.MethodBeforeAdvice前置增强
- 目标接口和实现类
package com.lyc.cn.v2.day04.advisor; /** * @author: LiYanChao * @create: 2018-11-01 21:48 */ public interface Animal { void sayHello(String name,int age); void sayException(String name,int age); }
package com.lyc.cn.v2.day04.advisor; /** * @author: LiYanChao * @create: 2018-11-01 21:48 */ public class Dog implements Animal { @Override public void sayHello(String name, int age) { System.out.println("==名字:" + name + " 年龄:" + age); } @Override public void sayException(String name, int age) { System.out.println("==名字:" + name + " 年龄:" + age); System.out.println("==抛出异常:" + 1 / 0); } }
- 前置增强
package com.lyc.cn.v2.day04.advisor; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; /** * 前置增强 * @author: LiYanChao * @create: 2018-11-01 21:50 */ public class MyMethodBeforeAdvice implements MethodBeforeAdvice { /** * Callback before a given method is invoked. * @param method method being invoked * @param args arguments to the method * @param target target of the method invocation. May be {@code null}. * @throws Throwable if this object wishes to abort the call. * Any exception thrown will be returned to the caller if it's * allowed by the method signature. Otherwise the exception * will be wrapped as a runtime exception. */ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("==前置增强"); System.out.println("==方法名:" + method.getName()); if (null != args && args.length > 0) { for (int i = 0; i < args.length; i++) { System.out.println("==第" + (i + 1) + "参数:" + args[i]); } } System.out.println("==目标类信息:" + target.toString()); } }
4.AfterReturningAdvice后置增强
package com.lyc.cn.v2.day04.advisor; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; /** * 后置增强 * @author: LiYanChao * @create: 2018-11-01 22:09 */ public class MyAfterReturningAdvice implements AfterReturningAdvice { /** * Callback after a given method successfully returned. * @param returnValue the value returned by the method, if any * @param method method being invoked * @param args arguments to the method * @param target target of the method invocation. May be {@code null}. * @throws Throwable if this object wishes to abort the call. * Any exception thrown will be returned to the caller if it's * allowed by the method signature. Otherwise the exception * will be wrapped as a runtime exception. */ @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("==后置增强"); System.out.println("==方法名:" + method.getName()); if (null != args && args.length > 0) { for (int i = 0; i < args.length; i++) { System.out.println("==第" + (i + 1) + "参数:" + args[i]); } } System.out.println("==目标类信息:" + target.toString()); } }
5.ThrowsAdvice异常增强
package com.lyc.cn.v2.day04.advisor; import org.springframework.aop.ThrowsAdvice; import java.lang.reflect.Method; /** * @author: LiYanChao * @create: 2018-11-01 22:17 */ public class MyThrowsAdvice implements ThrowsAdvice { /** * 异常增强 */ public void afterThrowing(Method method, Object[] args, Object target, Exception ex) { System.out.println("==异常增强"); System.out.println("==方法名:" + method.getName()); if (null != args && args.length > 0) { for (int i = 0; i < args.length; i++) { System.out.println("==第" + (i + 1) + "参数:" + args[i]); } } System.out.println("==目标类信息:" + target.toString()); System.out.println("==异常信息:" + ex.toString()); } }
6.MethodInterceptor环绕增强
package com.lyc.cn.v2.day04.advisor; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; /** * 环绕增强 * @author: LiYanChao * @create: 2018-11-01 22:29 */ public class MyMethodInterceptor implements MethodInterceptor { /** * 环绕增强 这里的方法参数与之前的前置增强、后置增强明显不同,只有一个MethodInvocation类型的参数 * Implement this method to perform extra treatments before and * after the invocation. Polite implementations would certainly * like to invoke {@link Joinpoint#proceed()}. * @param invocation the method invocation joinpoint * @return the result of the call to {@link Joinpoint#proceed()}; * might be intercepted by the interceptor * @throws Throwable if the interceptors or the target object * throws an exception */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("==环绕增强开始"); System.out.println("==方法名:" + invocation.getMethod().getName()); Object[] args = invocation.getArguments(); if (null != args && args.length > 0) { for (int i = 0; i < args.length; i++) { System.out.println("==第" + (i + 1) + "参数:" + args[i]); } } Object proceed = invocation.proceed(); System.out.println("==环绕增强结束"); return proceed; } }
7.测试及结果
@Test public void test5() { // 前置增强 // 1、实例化bean和增强 Animal dog = new Dog(); MyMethodBeforeAdvice advice = new MyMethodBeforeAdvice(); // 2、创建ProxyFactory并设置代理目标和增强 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(dog); proxyFactory.addAdvice(advice); // 3、生成代理实例 Animal proxyDog = (Animal) proxyFactory.getProxy(); proxyDog.sayHello("二哈", 3); } @Test public void test6() { // 后置增强 // 1、实例化bean和增强 Animal dog = new Dog(); MyAfterReturningAdvice advice = new MyAfterReturningAdvice(); // 2、创建ProxyFactory并设置代理目标和增强 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(dog); proxyFactory.addAdvice(advice); // 3、生成代理实例 Animal proxyDog = (Animal) proxyFactory.getProxy(); proxyDog.sayHello("二哈", 3); } @Test public void test7() { // 异常增强 // 1、实例化bean和增强 Animal dog = new Dog(); MyThrowsAdvice advice = new MyThrowsAdvice(); // 2、创建ProxyFactory并设置代理目标和增强 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(dog); proxyFactory.addAdvice(advice); // 3、生成代理实例 Animal proxyDog = (Animal) proxyFactory.getProxy(); proxyDog.sayException("二哈", 3); } @Test public void test8() { // 环绕增强 // 1、实例化bean和增强 Animal dog = new Dog(); MyMethodInterceptor advice = new MyMethodInterceptor(); // 2、创建ProxyFactory并设置代理目标和增强 ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(dog); proxyFactory.addAdvice(advice); // 3、生成代理实例 Animal proxyDog = (Animal) proxyFactory.getProxy(); proxyDog.sayHello("二哈", 3); }
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@2280cdac] ==前置增强 ==方法名:sayHello ==第1参数:二哈 ==第2参数:3 ==目标类信息:com.lyc.cn.v2.day04.advisor.Dog@2280cdac ==名字:二哈 年龄:3
十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy 信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@6b2fad11] ==名字:二哈 年龄:3 ==后置增强 ==方法名:sayHello ==第1参数:二哈 ==第2参数:3 ==目标类信息:com.lyc.cn.v2.day04.advisor.Dog@6b2fad11
十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy 信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@38082d64] ==名字:二哈 年龄:3 ==异常增强 ==方法名:sayException ==第1参数:二哈 ==第2参数:3 ==目标类信息:com.lyc.cn.v2.day04.advisor.Dog@38082d64 ==异常信息:java.lang.ArithmeticException: / by zero 十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy 信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@3f2a3a5] java.lang.ArithmeticException: / by zero at com.lyc.cn.v2.day04.advisor.Dog.sayException(Dog.java:17) at com.lyc.cn.v2.day04.advisor.Dog$$FastClassBySpringCGLIB$$a974b1ec.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
==环绕增强开始 ==方法名:sayHello ==第1参数:二哈 ==第2参数:3 ==名字:二哈 年龄:3 ==环绕增强结束
8.总结
以上简单介绍了前置增强、后置增强、环绕增强、异常增强等以编码实现的方式,当然以上实现也可以通过配置文件的方式实现。本篇所介绍的知识点比较简单,但是理解增强的概念是AOP的基础,其实本篇的各种增强方式用上一篇讲解的动态代理是完全可以实现的,因为本篇使用的代理工厂ProxyFactory
内部使用的是CglibAopProxy
或JdkDynamicAopProxy
,其实其本质还是JDK或CGLIB动态代理,在接下来的章节中会详细讲解CglibAopProxy
或JdkDynamicAopProxy
的实现,本篇的分析就到这里了。