栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

一文搞懂AOP调用原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一文搞懂AOP调用原理

一. 前言

AOP是spring框架的两大核心之一,在日常工作中也会经常会到它,典型的应用用来记录日志,包括spring的注解事务也是通过AOP实现的。大家是否都对AOP原理都很清楚呢?AOP底层到底是如何实现的?AOP在什么场景下会失效?

二. AOP失效例子

带着这些疑问,我们从一个AOP失效的例子开始揭开AOP的面纱。

  1. 定义一个切点
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface AopTest {

    String value() default "";
}
  1. 定义一个切面
@Aspect
@Component
public class AopAspect {

    @Pointcut("@annotation(com.zsc.aspect.AopTest)")
    public void pointCut() {}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable     {
        System.out.println("-----------around before-----------");
        Object proceed = point.proceed();
        System.out.println("-----------around after-----------");
        return proceed;
    }
}
  1. 写一个被切service
@Service
public class AopService {

    @Autowired
    ApplicationContext applicationContext;

    @AopTest(value = "test1")
    public void test1() {
        System.out.println("aop service test1 method");
        test2();
    }

    @AopTest(value = "test12")
    public void test2() {
        System.out.println("aop service test2 method");
    }
}
  1. 写一个测试类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FormApplication.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class AopServiceTest {

    @Resource
    AopService aopService;

    @Test
    public void test1() {
        aopService.test1();
    }
}
  1. 运行结果

    结果:从代码简单分析可以得出结论,test1方法的切面生效,test2方法的切面未生效。
  2. 分析
    通过打断点看看test1和test2方法的调用对象分别是什么

    test1方法的调用对象是AopService的代理对象

    test2方法的调用对象是AopService的直接对象
    所以很容易理解为什么test2方法的切面失效,因为调用test2方法的对象不是代理对象,故不能执行切面的代码。
  3. 解决方案
    解决方法思路很简单,就是要用代理对象去调用test2方法,这里给出最简单的方法
三. AOP调用原理

开始之前先添加一些切面方法

@Aspect
@Component
public class AopAspect {

    @Pointcut("@annotation(com.zsc.aspect.AopTest)")
    public void pointCut() {

    }

    @Before("pointCut()")
    public void before(JoinPoint point) {
        System.out.println("before");
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("-----------around before-----------");
        Object proceed = point.proceed();
        System.out.println("-----------around after-----------");
        return proceed;
    }

    @AfterReturning(value = "pointCut()")
    public void afterReturning(JoinPoint joinPoint) {
        System.out.println("afterReturning advise");
    }

    @AfterThrowing(value = "pointCut()")
    public void afterThrowing(JoinPoint joinPoint) {
        System.out.println("afterThrowing advise");
    }

    @After(value = "pointCut()")
    public void after(JoinPoint joinPoint) {
        System.out.println("after advise");
    }
}

解决了上面的AOP失效问题,继续探索AOP的动态代理及Before、Around、After、AfterReturning、AfterThrowing等切面逻辑是如何执行的。

  1. 查看spring生成的代理对象内容

    添加系统属性,保存spring生成代理类的class文件,注意:必需加在static静态块里,不能直接加在单元测试test1方法里,因为执行test1方法时,代理类已经生成结束了。运行单元测试方法,然后在指定目录可以找到生成的代理类,将生成的代理类直接拖到idea中进行反编译。

    反编译后的代理类中的test1和test2方法代码很少而且逻辑完全一样的。

    继续debug,可以看到调用代理类的test1方法时实际上就是调用代理类中的DynamicAdvisedInterceptor类的intercept方法
@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;
      }
      // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
      target = targetSource.getTarget();
      Class targetClass = (target != null ? target.getClass() : null);
	  // 获取所有before,after,around等切面
      List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
      Object retVal;
      // Check whether we only have one InvokerInterceptor: that is,
      // no real advice, but just reflective invocation of the target.
      if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
         // We can skip creating a MethodInvocation: just invoke the target directly.
         // Note that the final invoker must be an InvokerInterceptor, so we know
         // it does nothing but a reflective operation on the target, and no hot
         // swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = methodProxy.invoke(target, argsToUse);
      }
      else {
         // We need to create a method invocation...
	     // 通过上面获取到的切面列表生成切面方法调用链并执行
         retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
      }
      // 构建返回对象
      retVal = processReturnType(proxy, target, method, retVal);
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}
 

继续debug

调用了CglibMethodInvocation的proceed方法,该方法直接调用了其父类ReflectiveMethodInvocation的proceed方法

@Override
@Nullable
public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
	  // 切面调用链执行完,调用被切面方法
      return invokeJoinpoint();
   }

	// 使用currentInterceptorIndex计数完成调用链模式,而没有使用for循环
   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.
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

ReflectiveMethodInvocation有几个比较重要的属性

最后通过 AspectJAroundAdvice、AspectJAfterThrowingAdvice、MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、AspectJAfterAdvice等类中的invoke方法可以明白如何做的前置切面、后置切面、环绕切面等。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号