Spring AOP 学习记录1

主题

学习记录一下spring aop是怎么一个流程

以传统XML方式, ClassPathXmlApplicationContext为例子来学习.

 

 

  

BeanPostProcessor

spring很多操作都要依赖这些特殊的Bean.

在XML里有 <aop:aspectj-autoproxy /> 这个标记的时候会被AopNamespaceHandler解析

 

它会注册特殊的Bean AnnotationAwareAspectJAutoProxyCreator

 

 

 

 

 

 

BeanPostProcessor可以在适当的时机替换掉原本的bean,并返回proxy.

所以我觉得这里最核心的大概就是覆盖了BPP的 postProcessAfterInitialization 方法

返回了AOP代理的proxy bean. 这个时间点就是原始的Bean已经new完,并且所有属性已经被populate了.而且所有initialize方法已经做过(init-method, afterPropertiesSet等方法).了以后.

 

 

  

如上图,specificInterceptors里返回的是可以作用于当前这个Bean的advisor.

advisor=advice + pointcut (filter)

advice就是我们定义的操作.比如before..after..around等等

pointcut就是当前bean对应的这个类可能会对应N个advice,并不是所有的方法都要应用每个advice..每个advice需要满足一些条件才可以被触发..一般都是用Pointcut的表达式去描述这个条件..

所以我们的advisor一般都是PointcutAdvisor.

 

有了Advisor,也有了当前原始的raw Bean.那我们就要组合他们创建一个proxy,并返回. 这就是createProxy那个方法要做的事情.

 

ProxyFactory

具体去创建proxy是委托ProxyFactory来操作的....通过配置他的各种属性,比如setTargetClass, setAdvisor...

往里面各种set.然后就可以getProxy了..

具体怎么操作的呢?

 

首先需要根据配置获取AopProxy,AopProxy有2种实现,一种cglib,一种jdk动态代理.

选哪种就是根据配置来的.判断条件如下.

private boolean optimize = false; 默认是不优化的.(定义在ProxyConfig里)

proxyTargetClass默认也是false.

如果你代理的bean有impl Interface.那就会用jdk动态代理

如果想用cglib.最最最简单的就是修改proxyTargetClass这个属性.在XML里就可以配置.

从中也可以看出,如果想用JDK动态代理.你的类只要要impl一个interface.不然hasNoUserSuppliedInterfaces就为true了.

 

最终返回的是Proxy创建的代理. 

 

 

 

JdkDynamicAopProxy

以JdkDynamicAopProxy为例,它本身实现了InvocationHandler了.最终proxy的方法被调用的时候.就回进JdkDynamicAopProxy的invoke方法.

 

 

 1 /**
 2      * Implementation of {@code InvocationHandler.invoke}.
 3      * <p>Callers will see exactly the exception thrown by the target,
 4      * unless a hook method throws an exception.
 5      */
 6     @Override
 7     @Nullable
 8     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 9         Object oldProxy = null;
10         boolean setProxyContext = false;
11 
12         TargetSource targetSource = this.advised.targetSource;
13         Object target = null;
14 
15         try {
16             if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
17                 // The target does not implement the equals(Object) method itself.
18                 return equals(args[0]);
19             }
20             else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
21                 // The target does not implement the hashCode() method itself.
22                 return hashCode();
23             }
24             else if (method.getDeclaringClass() == DecoratingProxy.class) {
25                 // There is only getDecoratedClass() declared -> dispatch to proxy config.
26                 return AopProxyUtils.ultimateTargetClass(this.advised);
27             }
28             else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
29                     method.getDeclaringClass().isAssignableFrom(Advised.class)) {
30                 // Service invocations on ProxyConfig with the proxy config...
31                 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
32             }
33 
34             Object retVal;
35 
36             if (this.advised.exposeProxy) {
37                 // Make invocation available if necessary.
38                 oldProxy = AopContext.setCurrentProxy(proxy);
39                 setProxyContext = true;
40             }
41 
42             // Get as late as possible to minimize the time we "own" the target,
43             // in case it comes from a pool.
44             target = targetSource.getTarget();
45             Class<?> targetClass = (target != null ? target.getClass() : null);
46 
47             // Get the interception chain for this method.
48             List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
49 
50             // Check whether we have any advice. If we don't, we can fallback on direct
51             // reflective invocation of the target, and avoid creating a MethodInvocation.
52             if (chain.isEmpty()) {
53                 // We can skip creating a MethodInvocation: just invoke the target directly
54                 // Note that the final invoker must be an InvokerInterceptor so we know it does
55                 // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
56                 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
57                 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
58             }
59             else {
60                 // We need to create a method invocation...
61                 MethodInvocation invocation =
62                         new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
63                 // Proceed to the joinpoint through the interceptor chain.
64                 retVal = invocation.proceed();
65             }
66 
67             // Massage return value if necessary.
68             Class<?> returnType = method.getReturnType();
69             if (retVal != null && retVal == target &&
70                     returnType != Object.class && returnType.isInstance(proxy) &&
71                     !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
72                 // Special case: it returned "this" and the return type of the method
73                 // is type-compatible. Note that we can't help if the target sets
74                 // a reference to itself in another returned object.
75                 retVal = proxy;
76             }
77             else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
78                 throw new AopInvocationException(
79                         "Null return value from advice does not match primitive return type for: " + method);
80             }
81             return retVal;
82         }
83         finally {
84             if (target != null && !targetSource.isStatic()) {
85                 // Must have come from TargetSource.
86                 targetSource.releaseTarget(target);
87             }
88             if (setProxyContext) {
89                 // Restore old proxy.
90                 AopContext.setCurrentProxy(oldProxy);
91             }
92         }
93     }

L48 获取这次方法调用可以使用的advice.

对于当前这个bean的method的调用. 首先把所有的advisor先拿出来.然后再取出里面的pointcut.一个一个判断,是不是可以作用于当前的method.如果可以的话.那OK.这次方法调用应该要invoke对应的advice(advisor).

不然就应该略过这个advisor.

 

advice会被包装成对应的intercetor.

比如MethodBeforeAdvice会对应MethodBeforeAdviceInterceptor,

AfterReturningAdvice会对应AfterReturningAdviceInterceptor.

而AspectJAroundAdvice本身就是一个MethodInterceptor

why?

以MethodBeforeAdvice举例.接口里有具体的advice方法void before(Method method, Object[] args, @Nullable Object target) throws Throwable;

这个方法里就是要业务开发自己去实现对应的连接点操作之前运行的逻辑.

那么为什么这个before方法就是会在连接点之前运行? 这个逻辑是在哪里控制的? 这个怎么保证的?

这个就是在intercetor里操作的

 

 其他advice和interceptor类似.

因为around的逻辑是用户自己写的.所以它本身就是一个MethodInterceptor.(想想我们是不是会在方法里取调用mi.proceed方法)

 

L61, 62

如果chain不是empty的.说明确实有要invoke的advice.

那就把这次 方法调用+advice chain封装成一个ReflectiveMethodInvocation

这个思想就像责任链(也有一点点组合的味道.把整个list封装到一个对象里去调用单个对象的方法).

对比servlet的filter.

每个advice对应的intercetor就是每个filter

ReflectiveMethodInvocation就是filterChain

 

为什么在每一个interceptor里调用mi.proceed就会推进到下一个interceptor的方法调用和原本方法的调用? 这个逻辑是在哪里操作的? 这个逻辑就是由ReflectiveMethodInvocation来操作的.

 

它维护了一个指针,指向当前的interceptor. 并invoke它的方法.

当在每一个interceptor里调用mi.proceed的时候, 这里的mi就是ReflectiveMethodInvocation..每次proceed的时候,指针都会++,于是就能找到对应的intercetor.

 

 

小结

spring aop代理是通过特殊的BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)来实现的.

它实现了postProcessAfterInitialization方法.在Bean new完,属性populate完.initialize相关方法都调用完后通过ProxyFactory来创建代理对象.

在Bean相关method被调用的时候通过ReflectiveMethodInvocation找到需要激活的advise(interceptor)并调用invoke方法完成AOP功能.

 

posted @ 2020-04-08 20:02  abcwt112  阅读(168)  评论(0编辑  收藏  举报