Spring Aop

121 阅读7分钟

0.本文重点

  • 1.spring如何织入增强,并创建代理对象
  • 2.spring如何选择cglib代理还是jdk代理
  • 3.jdk代理执行过程

1.例子及概念

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {

    String value() default "";

}

@Aspect
@Slf4j
@Component
public class LogAspect {

    @Pointcut("@annotation(logAnnotation)")
    public void pointCut(LogAnnotation logAnnotation){}

    @Before("pointCut(logAnnotation)")
    public void before(JoinPoint joinPoint, LogAnnotation logAnnotation){
        log.info("process before method, remark is :{}",logAnnotation.value());
    }

    @After("pointCut(logAnnotation)")
    public void after(JoinPoint joinPoint, LogAnnotation logAnnotation){
        log.info("process after method, remark is :{}",logAnnotation.value());
    }

    @Around("pointCut(logAnnotation)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, LogAnnotation logAnnotation) throws Throwable {
        log.info("process around-before method, remark is :{}",logAnnotation.value());
        Object proceed = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        log.info("process around-after method, remark is :{},result is :{}",logAnnotation.value(),proceed);
        return proceed;
    }

    @AfterReturning("pointCut(logAnnotation)")
    public void afterReturning(JoinPoint joinPoint, LogAnnotation logAnnotation){
        log.info("process afterReturning method, remark is :{}",logAnnotation.value());
    }
}


@Override
@LogAnnotation("purchase")
public String purchase(){
    orderService.place();
    storeService.deduct();
    return "1";
}

@Override
public void cancel() {
    log.info("取消订单");
}

切点、切面、增强在上述例子中的体现:

增强/通知 (advice): before、after等声明了特定注解的方法 ,不仅仅是方法本身,方法与其携带的注解
   会被解析为不同类型的Advice
切点: 声明@Pointcut 的方法
Advisor: 增强方法 + 切点 
切面:@Aspect注解所在类,其携带Around, Before, After, AfterReturning, AfterThrowing注解的方法会被解析成 Advisor

除@annotation方式声明切点外,还可以

image.png

通常 "." 代表一个包名,".." 代表包及其子包,方法参数任意匹配使用两个点 ".."。

2.创建代理对象

前面文章将spring ioc机制时,有提到过 AbstractAutoProxyCreator(AnnotationAwareAspectJAutoProxyCreator)在initializeBean阶段完成代理化

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      //循环依赖时会将原始对象存放在此,如果remove != ,那么没有提前完成代理化,继续走
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   //当产生循环依赖时,上述的remove 结果相等,直接返回原始对象,最终依赖二级缓存获取代理对象,
   如果有不理解的地方 可以去看 spring ioc处理机制 文章结尾 介绍三级缓存部分
   return bean;
}

2.1 wrapIfNecessary

AbstractAutoProxyCreator#wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    ...
    //已经判断过不需要增强 或者 本身是 Advice/Pointcut/Advisor/AopInfrastructureBeank
      及有@Aspect注解 这类特殊的bean
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   //本身是 Advice/Pointcut/Advisor/AopInfrastructureBeank 或有@Aspect注解
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // 查找增强  内部调用   AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      //只要不为空表示 表示需要创建代理对象
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      //创建代理对象
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }
   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

2.1.1 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   //内部会调用 BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors 扫描当前容器中所有带
     @Aspect注解的bean 调用 ReflectiveAspectJAdvisorFactory#getAdvisors 将bean内部方法包装为Advisor 。
     除此之外还有容器内已创建的Advisor,比如 BeanFactoryTransactionAttributeSourceAdvisor
     这里最终返回容器内所有Advisor
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   //PointcutAdvisor是Advisor的一个实现类,这里的例子只涉及PointcutAdvisor  
    PointcutAdvisor = 增强 + 切点,所以这里会遍历 所有Advisor,根据其内部携带的切点,遍历所有方法 只要有一个方法符合切点作用范围,则代表该bean受Advisor作用
    //以@Transactional为例 Advisor是 BeanFactoryTransactionAttributeSourceAdvisor  
      内部的 pointCut 是TransactionAttributeSourcePointcut
      基于 SpringTransactionAnnotationParser 判断公有方法 或类上是否包含 Transactional 
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   //在头部添加一个DefaultPointcutAdvisor(advice为 ExposeInvocationInterceptor)
   extendAdvisors(eligibleAdvisors);
   ...
   return eligibleAdvisors;
}

2.1.1.1 ReflectiveAspectJAdvisorFactory#getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    ...
   List<Advisor> advisors = new ArrayList<>();
   //获取其内部不携带 @Pointcut 注解的方法
   for (Method method : getAdvisorMethods(aspectClass)) {
     //根据方法上的 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
       注解,将注解信息包装为 AspectJExpressionPointcut(切点:注解内容+class属性),继而将方法与AspectJExpressionPointcut
       一同包装为 InstantiationModelAwarePointcutAdvisorImpl(Advisor)
      Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
      if (advisor != null) {
         advisors.add(advisor);
      }
   }
   ...
   return advisors;
}

DefaultPointcutAdvisor  是Spring提供的通用的 Advisor。它可以把任意的Advice和Pointcut放在一起

BeanFactoryCacheOperationSourceAdvisor:和Cache有关  @Caching @Cacheable @CacheEvict @CachePut

InstantiationModelAwarePointcutAdvisor 解析被 @Aspect注解注释的类时生成的 Advisor,。而这个 Advisor中的 Pointcut与Advice都是由ReflectiveAspectJAdvisorFactory 来解析生成的(与之对应的 Advice 是 AspectJMethodBeforeAdvice, AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice, AspectJAroundAdvice,     Pointcut 则是AspectJExpressionPointcut)

IntroductionAdvisor 一个Java类,没有实现A接口,在不修改Java类的情况下,使其具备A接口的功能。仅有一个类过滤器ClassFilter 而没有 MethodMatcher,因为它的切点是类级别的,而 Pointcut 的切点是方法级别的(细粒度更细)。与之一起的是 IntroductionInterceptor:引介拦截器。

2.1.2 AbstractAutoProxyCreator#createProxy

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }
   ProxyFactory proxyFactory = new ProxyFactory();
   //this = AnnotationAwareAspectJAutoProxyCreator,本身是ProxyConfig对象,
   proxyFactory.copyFrom(this);
   //proxyTargetClass 为true时默认使用cglib 为false默认使用jdk 
    注:spring boot 2.0后 注解@EnableAspectJAutoProxy(proxyTargetClass = false) 声明已不再有效,需要使用配置 spring.aop.proxy-target-class 详情可见 AopAutoConfiguration
   if (!proxyFactory.isProxyTargetClass()) {
      //如果显著声明 期望优先走jdk代理 ,那就判断 当前bean是否支持jdk代理
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         //判断是否具备有效的接口  
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   proxyFactory.addAdvisors(advisors);
   proxyFactory.setTargetSource(targetSource);
   //空方法 子类可重写
   customizeProxyFactory(proxyFactory);

   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }
    //调用 DefaultAopProxyFactory#createAopProxy 创建代理对象
   return proxyFactory.getProxy(getProxyClassLoader());
}

2.1.2.1 ProxyProcessorSupport#evaluateProxyInterfaces

protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
   //获取所有接口
  Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
   boolean hasReasonableProxyInterface = false;
   for (Class<?> ifc : targetInterfaces) {
      //只要有 非空 普通接口 就返回 ,即 可以走jdk代理
      if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
            ifc.getMethods().length > 0) {
         hasReasonableProxyInterface = true;
         break;
      }
   }
   if (hasReasonableProxyInterface) {
      for (Class<?> ifc : targetInterfaces) {
         proxyFactory.addInterface(ifc);
      }
   }
   else {
      //没有有效的接口 那就把配置改为true 后面还需要根据这个属性进行判断
      proxyFactory.setProxyTargetClass(true);
   }
}

2.1.1.2 DefaultAopProxyFactory#createAopProxy

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   //proxyTargetClass为true  或者  没有接口 或者只有一个接口且为SpringProxy 
   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
     //如果本身是一个接口 或者 代理对象 会使用jdk代理 ,否则使用 CGLIB 实现代理
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

综上我们可以看到spring创建代理对象,以及决定代理方式的过程。 代理一个servieImpl bean时,主要还是根据 proxyTargetClass属性 (受spring.aop.proxy-target-class 配置影响,如果你愿意,专门创建一个MergedBeanDefinitionPostProcessor 或InstantiationAwareBeanPostProcessor 在属性注入前修改对应值也是可以的) 来决定代理方式。一般默认走cglib代理。cglib代理从代码管理上的好处是能兼容 注入接口类/子类的场景。如果是jdk代理,注入的对象是子类的话就会报错,因为jdk代理的是接口,而不是实现类。

3.JdkDynamicAopProxy

public Object getProxy(@Nullable ClassLoader classLoader) {
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   //是否显著声明了hashcode 和 equals方法
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   //第三个参数 InvocationHandler  代理对象的方法调用执行 都是委托给InvocationHandler执行的
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

3.1 JdkDynamicAopProxy#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;

   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      ..hashcode equals 方法 ..
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         //如果是Advised接口方法  直接反射调用
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }
      Object retVal;
      if (this.advised.exposeProxy) {
        //如果设置了exposeProxy  将代理对象放到ThreadLocal  ,如EnableAspectJAutoProxy#exposeProxy
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);
      //根据Advisor 的 pointCut判断是否作用于该方法,同时将AfterReturningAdvice转化为 AfterReturningAdviceInterceptor,
        MethodBeforeAdvice转化为MethodBeforeAdviceInterceptor,ThrowsAdvice转化为 ThrowsAdviceInterceptor
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 
      if (chain.isEmpty()) {
         //不需要增强 反射调用
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         //基于拦截器链  调用
         retVal = invocation.proceed();
      }
      ...
   }
   finally {
      ...
      if (setProxyContext) {
         //threadlocal还原
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

image.png

3.1.1 ReflectiveMethodInvocation#proceed

public Object proceed() throws Throwable {
   //下标从-1开始递增 全部执行完毕后  反射调用被代理对象的目标方法 。
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }
   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   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)) {
         //调用interceptor(Advice)增强方法
         return dm.interceptor.invoke(this);
      }
      else { 
         return proceed();
      }
   }
   else {
      //前文extendAdvisors方法中添加的  DefaultPointcutAdvisor(advice为 ExposeInvocationInterceptor)  进入这里执行,将MethodInvocation 放入线程变量Threadlocal中
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

总结: 创建代理过程:

bean实例化 在initializeBean 阶段, 调用所有的BeanPostProcessor . postProcessAfterInitialization 方法,AnnotationAwareAspectJAutoProxyCreator 在这里尝试进行代理化 (如果有循环依赖,在populate 注入阶段会提前触发代理化)

如果bean不是类似Advisor,Advice之类的实例,获取所有Advisor, 根据pointCut切点判断是否能作用于bean,如果有适配的Advisor,则表示该bean需要代理

如果proxyTargetClass设置为false(springboot默认走cglib代理),遍历bean的接口,判断是不是有可用的接口(非空 普通接口  类似InitializingBean,DisposableBean,Aware都不算),如果没有 设置 proxyTargetClass为true 

调用 DefaultAopProxyFactory .createProxy方法创建代理 ,如果 proxyTargetClass 为true 或者没有接口 ,  且 bean 不是接口 也不是代理对象,走cglib代理 ,其他走jdk代理  ,返回 AopProxy对象 ,其getProxy方法返回代理对象

以JdkDynamicAopProxy为例 ,调用 getProxy方法时  内部调用 Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);  (最终基于JdkDynamicAopProxy.invoke 代理目标方法)

其他

web层,也就是controller处 使用@Transactional注解,刚好 路由方法声明为private 会发生什么?

前面的文章提到过 路由注册 只看@RequestMapping注解,私有方法也是可以注册为路由的。 最终会发现 方法中调用的service全部为空。

@Transactional注解使 controller 成为一个代理对象,由于controller无接口且默认实现为cglib代理,方法为私有方法,不被继承,所以不会调用到 目标对象,最终的调用形式就变成了下面所示

public class Foo {
    String name;
    private String getName(){
        System.out.println("name :" + name);
        return name;
    }
}

 class Bar extends Foo{
}

public static void main(String[] args) throws Exception {
    Foo foo = new Foo();
    foo.name = "qwe";
    Bar bar = new Bar();
    Method method = foo.getClass().getDeclaredMethod("getName");
    method.setAccessible(true);
    method.invoke(bar);
}
```