Spring中AOP源码深度解读_程序员李哈的博客-CSDN博客目录为什么Spring中存在AOP什么是面向切面什么是动态代理AOP源码解读总结为什么Spring中存在AOP凡是学一样东西,要知其先后。对于Spring来说,其中2大核心IoC和AoP。对于IoC来说控制反转,对于项目中的类(bean)你直接交给Spring就行了,你想使用直接从我这里拿。当项目不断的更新迭代,需要在原有的基础上不断的增加逻辑。可能没有AoP之前,对于程序员来说可能会使用代理模式来对其解耦,此时又有啥静态代理,动态代理的。那么,对于Spring的开发人员https://blog.csdn.net/qq_43799161/article/details/124131569?spm=1001.2014.3001.5502
上篇文章,非常仔细的讲解到AOP的一些概念的理解,和Spring中AOP如何创建代理,并且从反编译字节码中推理出代理方法的MethodInterceptor方法拦截。所以此篇文章继续讲解方法拦截的详细步骤,并且也会对上篇文章的一些点做回顾。
@Component
public class Test {
public void test(){
System.out.println("这是test的内容");
}
}
@Aspect // 声明这是一个切面
@Component
public class Section { // 切面
@Before(value = ("execution(* com.liha.bean.Test.test(..))")) // 切点
public void testProxy(){
System.out.println("在之前"); // 通知
}
}
这样就可以生成代理类的字节码文件了,然后通过IDEA反编译。
所以看到0号方法拦截的具体逻辑。
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 获取到target的类对象,也就是被代理类
target = targetSource.getTarget();
// 获取到被代理类的Class对象。
Class> targetClass = (target != null ? target.getClass() : null);
// 使用MethodMatcher的matches()方法对当前方法做切点的匹配,
// 因为当前代理类中方法可能存在需要被切面增强的,也存在没有被增强的
// 所以这里就通过ProxyFactory这个Advised的子类获取到所有的切点+通知,
// 上篇文章有介绍在代理之前把当前代理类中对应的所有切点+通知给放入到ProxyFactory中
List
大致流程如下(详细的流程看代码块中的注释):
CglibMethodInvocation对象好比就是Aop中真正执行切面增强逻辑+原本方法逻辑的调度者。内部使用递归的方式,把我们的切点+通知包装成一个链来递归处理。因为在Aop中通知,分为前后通知、环绕通知、异常通知、返回值后通知。所以这里就是把他们串成一个链来处理。并且这些通知也在前面的操作以及解析完毕,目前就只剩执行了。所以我们看到proceed()方法。
@Override
@Nullable
public Object proceed() throws Throwable {
// currentInterceptorIndex的初始值为-1,可以理解为索引下标
// this.interceptorsAndDynamicMethodMatchers就是我们的切点+通知,前面解析成一个List集合
// 在我的案例里是存在一个Before的通知,但是在Aop中会自带一个切点+通知,所以我的案例是存在2个
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 能进到这个if中,就代表要执行目标方法,也就是被增强的方法逻辑
return invokeJoinpoint();
}
// 这里取List集合中的值,也就是取切点+通知
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 这边逻辑,笔者不是特别清楚,但是我有追踪到创建处,
// 也就是通过方法来匹对切点的时候会对其做判断,但是永远返回的为false,所以这里是不存在的,所以直接看到else代码块中
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
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代码块中
else {
// 执行通知的逻辑了。 我项目中是before,但是笔者会把其他的通知也展示出来,并且说明区别
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
这里也就是通过索引来控制,通知的逻辑和目标方法的逻辑前后执行。然后每次递归就会取到切点链中的数据,除非第一个if已经通过了,也就是执行了目标方法了。就代表切点链已经遍历完毕了(但是并没有执行完所有的切点链(因为是递归的),因为还有后置,环绕之类的通知)。
所以我们直接看到不同的通知的invoke执行的逻辑区别。
前置通知:
后置通知:
返回值后通知
其他的就不一一介绍了,大家可以看invoke方法的各种子类的实现。
大家一定要思考是递归的场景,并且可以debug来走N遍流程理解,并且可以根据IDEA的debug的工具带有栈帧功能快速定位。
没啥总结,这里稍微复杂就是递归这块,希望大家使用debug好好理解~!
最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!