共计 5534 个字符,预计需要花费 14 分钟才能阅读完成。
1 利用
日志、事务、限流、统计、上下文切换、异步等非业务性能的逻辑解决都能够用
2 例子
网上随处可见,这里只是简略举个例子
@Aspect
@Component
public class DemoAspect {@Pointcut("execution(* com.example.demo2.controller.DemoController.sayDemo())")
public void aa(){System.out.println("pointcut");
}
@Before("aa()")
public void beforeDemo(JoinPoint joinPoint){System.out.println(joinPoint);
System.out.println("before demo");
}
@After("aa()")
public void afterDemo(){System.out.println("after demo");
}
@Around("aa()")
public void zaroundDemo(ProceedingJoinPoint joinPoint){System.out.println("demo around1");
try {joinPoint.proceed();
} catch (Throwable throwable) {throwable.printStackTrace();
}
System.out.println("demo around2");
}
}
3 相干重要类
AbstractAspectJAdvise
AspectJAroundAdvice
AspectJMethodBeforeAdvice
AspectJAfterThrowingAdvice
AspectJAfterAdvice
AspectjAroundAdvice — 继承自 methodInterceptor
AspectjMethodBeforeAdvice — 这还有一个 MethodBeforeInterceptor 用来实现动静代理
4 调用过程
- 通过 cglib 代理生成代理类,外面重写父类的办法,退出拦截器 DynamicCglibMethodInterceptor,这外面退出了对于这个类的所有织入告诉(advice)对应的拦截器,比方:AspectJMethodBeforeAdvice—MethodBeforeAdviceInterceptor;AspectJAroundAdvice 的拦截器就是自身;
- 在进行调用的时候,会先遍历所有的 Advice 对应的拦截器,调用外面的 invoke 办法;把以后的 CglibMethodInvocation 对象传下去;(典型责任链模式)
3. 重点:AspectJAroundAdvise 外面有一个 ProceedingJoinPoint 参数,这个是如何调用的呢?实际上外面在 Advice 外面都是通过返回调用了切面类中的告诉办法,method.invoke(obj,args);为了实现盘绕告诉呢?在初始化 MethodInvokcationProceedingJoinPoint 的时候,就把 CglibMethodInvocation 也传入进去了,不便调用链的持续运行;如果是调用链最初一个,那么 methodInvocationProceedingJoinPoint 调用实现后,间接返回到盘绕告诉办法外面,而后持续向下执行,直至完结即可。
5 源码解析
5.1 动静代理类
依据
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,fileName);
生成的动静代理对象,能够看出,外面会有一个 DemoController$$EnhanceByCglib 这个对象,外面有一个 callback[] 数组,这个就是记录的所有拦截器;
通过动静生成的代理类调用 demo 办法的时候,实际上走的流程是:
public final void sayDemo() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
# 如果有拦截器,则调用拦截器办法,if (var10000 != null) {var10000.intercept(this, CGLIB$sayDemo$0$Method, CGLIB$emptyArgs, CGLIB$sayDemo$0$Proxy);
} else {super.sayDemo();
}
}
能够看到动静代理产生的类重写了父类的 DemoController 中的 sayDemo 办法,而后有写了一个办法叫
final void CGLIB$sayDemo$0() {super.sayDemo();
}
来调用父类的办法;
重点看下拦截器是什么?怎么调用的?
5.2 DynamicAdvisedInterceptor
注入的拦截器,
在 intercept 办法中,会判断 advice 是否为空,如果不为空,则会调用
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
5.3 CglibMethodInvocation
调用父类 proceed 办法
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 如果是最初一个,那么就完事了。if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();
}
// 依据 index 找下一个 advice 是什么?Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// 走到这里,调用 advice 对应的 MethodInterceptor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
假如:第一个是盘绕告诉对应的拦截器
5.4 AspectJAroundAdvice
public Object invoke(MethodInvocation mi) throws Throwable {if (!(mi instanceof ProxyMethodInvocation)) {throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation:" + mi);
}
// 把以后的 CglibMethodInvocation 实例对象传到 ProceedingJoinPoint 中,不便前面来用;ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
// 利用反射调用切面中的盘绕告诉办法,(即上文的 zaroundDemo 办法)return invokeAdviceMethod(pjp, jpm, null, null);
}
代码如下
@Around("aa()")
public void zaroundDemo(ProceedingJoinPoint joinPoint){System.out.println("demo around1");
try {
// 这里就是调用 MethodInvocationProceedingJoinPoint;
joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();
}
System.out.println("demo around2");
}
MethodInvocationProceedingJoinPoint.proceed 办法,能够发现,实际上它调用的是下面作为成员变量传进来的 CglibMethodInvocation 对象;造成一个调用链;
public Object proceed() throws Throwable {return this.methodInvocation.invocableClone().proceed();}
5.5 AspectJMethodBeforeAdvice–MethodBeforeAdviceInterceptor
拦截器外面的逻辑:
- 先调用前置告诉的 before 办法,
- 再返回调用链的 CglibMethodInvocation– 持续责任链的调用;
public Object invoke(MethodInvocation mi) throws Throwable {this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();}
AspectJMethodBeforeAdvice.proceed
也是利用反射进行调用切面中的前置告诉;
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {invokeAdviceMethod(getJoinPointMatch(), null, null);
}
5.6 AspectJAfterAdvice
在 finally 外面在反射调用织入的后置告诉;
public Object invoke(MethodInvocation mi) throws Throwable {
try {return mi.proceed();
}
finally {invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
5.7 责任链的最初一步:–invokeJoinpoint
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();
}
invokeJoinPoint
protected Object invokeJoinpoint() throws Throwable {
// 如果通过 cglib 代理,则调用动静代理办法
if (this.methodProxy != null) {
try {return this.methodProxy.invoke(this.target, this.arguments);
}
catch (CodeGenerationException ex) {logFastClassGenerationFailure(this.method);
}
}
// 否则就通过反射调用
return super.invokeJoinpoint();}
6 总结
- AOP 大量应用反射,诸如:调用织入告诉
- spring 将所有的告诉放到一个 list 中,利用责任链模式进行逐个调用;
- 最初在触发 cglib 的动静代理,调用 MethodProxy.invoke 办法