前言
AOP全称是Aspect-Oriented Programming,对应到编程术语中就是面向切面的编程。简单来说就是执行一段代码之前以及之后分别插入另一段代码,从而实现对这段代码全方位监视。
术语
Jointpoint
连接点,抽象统一了method,constructor,field。比如最常用的Method,对应到类就是`ReflectiveMethodInvocation`,可以拦截方法调用,并添加自定义的advice。这个概念是aop内部封装的过程中用到,使用者不需要直接接触。
Pointcut
切入点,其实就是一个过滤器,其子类衍生出静态的切入点,动态切入点,基于表达式的切入点等。本质就是通过对类,方法,输入参数的解析,判断哪些是需要进行拦截的,哪些是可以被过滤的。
Advice
通知,或者增强,这两种翻译都可以强行解释,是为了实现拦截Jointpoint后具体的逻辑,比如常见的MethodInterceptor,对方法进行拦截。这个接口是使用者最关心的,直接关系到业务逻辑。
核心模块
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> </dependency>
实现思路
spring支持两种动态代理方案,1种是基于jdk的动态代理,第2种是基于cglib的动态代理。详细参考`DefaultAopProxyFactory`,spring基于3个条件来选择哪一种动态代理方案,只要符合其中一个条件spring就会采用基于cglib的方案。1.是否需要优化(基于cglib的动态代理方案执行性能比JDK的高);2.是否声明了代理对象是类(JDK不支持对类的动态代理,而cglib支持);3.没有代理接口声明;所有配置可以通过`org.springframework.aop.framework.ProxyFactory`配置。用户注册的adivce先被缓存在列表中(Interceptor是Advice接口的子接口),如果没有显示声明则会被封装成`DefaultPointcutAdvisor`,这里就涉及到Pointcut这个术语,默认的Pointcut允许拦截所有类和方法。
基于JDK的动态代理
核心类是`JDKDynamicAopProxy`,首先根据配置选择型的将spring自己的3个接口加入到代理接口,然后自己实现了JDK中的`InvocationHandler`接口。当JDK的动态代理回调`InvocationHandler`的时候,先判断是否是spring自己增加的几个接口,如果是则通过反射调用直接返回,如果不是则通过执行`JoinPoint`的子类`ReflectiveMethodInvocation`,回调第一个advice,之后的advice通过使用者自己触发调用,这样形成了一个调用链。需要注意的是,spring为了提高效率,额外拦截了hashcode和equals方法直接处理,也不会进入advice的拦截处理流程。假如我们有两个advice,那么实际的执行顺序为
advice1.before -> advice2.before -> poxy.process-> advice2.after -> advice1.after
详细demo参考
public class AopTest { public static void main(String[] args){ System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); test(); } public static void test(){ // 基于对类的代理 ProxyFactory proxyFactory = new ProxyFactory(); // target 表示被真正执行代理的实例 proxyFactory.setTarget(new AopTester()); proxyFactory.setInterfaces(AopInterface.class); // advice 可以对调用进行拦截从而实现aop proxyFactory.addAdvice(new MyInterceptor1()); proxyFactory.addAdvice(new MyInterceptor1()); AopInterface aopTester = (AopInterface) proxyFactory.getProxy(); System.out.println(aopTester.run("test")); } /** * 被代理的接口 */ public static interface AopInterface{ public String run(String s); } /** * 被代理的类 */ public static class AopTester implements AopInterface{ @Override public String run(String s){ return s+" ing ..."; } } /** * 拦截器1,也是advice,提供通知 */ public static class MyInterceptor1 implements MethodInterceptor{ @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("1 before"); Object obj = invocation.proceed(); System.out.println(obj); System.out.println("1 after"); return "1 "+obj; } } /** * 拦截器2,也是advice,提供通知 */ public static class MyInterceptor2 implements MethodInterceptor{ @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("2 before"); Object obj = invocation.proceed(); System.out.println(obj); System.out.println("2 after"); return "2 "+obj; } } }
总结
spring的aop以动态代理为技术基础,结合JDK和cglib的proxy方案,抽象出Pointcut和Advice两个核心接口,让使用者非常方便的控制需要拦截的内容以及拦截后的逻辑。
参考
版本 4.3.10.RELEASE