likes
comments
collection
share

SpringAOP源码解析

作者站长头像
站长
· 阅读数 70

本文使用SpringBoot版本(2.2.x版本),在该版本中AOP的代理方式默认全是CGLIB代理

简单的AOP程序示例

下面我讲结合一个简单的AOP案例来讲解AOP的源码。

假设我们现在有一个类CService,要对CServicehello开头的方法进行日志跟踪,需要在这些方法执行前入参信息,执行完毕后打印方法耗时。该怎么实现呢?

@Component
public class CService {
    public void helloC1(String name) {
        System.out.println("hello " + name);
    }

    public void helloC2(String name) {
        System.out.println("hello " + name);
    }

    public void method(String name) {
        System.out.println("hello " + name);
    }
}

AOP是最好的选择!对代码无侵入性,且方便扩展,下面就来实现以下这个需求,编写切面MyLogAspect

@Component
@Aspect
public class MyLogAspect {
    // 使用execution表达式,只拦截CService的hello开头的方法
    @Pointcut("execution(public * com.sjx.service.CService.hello*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pj) throws Throwable {
        StopWatch watch = new StopWatch();
        Object result = null;
        Object[] args = pj.getArgs(); // 获取源方法的参数
        System.out.printf("方法%s入参 %s ...%n", pj.getSignature(), CollectionUtils.arrayToList(args));
        watch.start();
        result = pj.proceed();
        watch.stop();
        System.out.println("方法耗时:\n" + watch.prettyPrint());
        return result;
    }
}

需要注意的是 使用@Aspect注解需要额外引入依赖org.aspectj:aspectjweaver:1.9.19(版本号自己决定)

启动应用执调用helloC1方法可以看到如下输出

SpringAOP源码解析

AOP源码分析

概念

众所周知,AOP中有这么几个概念:连接点切入点切面。 下面我使用前文的示例稍微解释一下AOP要用到的这几个名词

连接点:连接点其实就是每个方法再AOP中的抽象概念,即每个方法都是连接点。CService中每个方法都是一个连接点

切入点:就是一些连接点的集合。在上例中它使用execution表达式来定义:execution(public * com.sjx.service.CService.hello*(..))表示CService中所有hello开头且方法修饰符为public的方法。

切面:切面定义了增强方法的逻辑。对应MyLogAspect中的around方法

源码解析

我们知道,Spring是通过IOC容器管理Bean的,在Spring启动时,会实例化所有标记@Service @Component注解的类。阅读本文假设你已经熟悉了SpringIOC的知识,明白bean是怎么创建的。

Spring提供了BeanPostProcessor这个接口可以插手Bean的初始化逻辑。

而AOP的逻辑正是通过实现BeanPostProcessor,重写postProcessAfterInitialization方法来完成在Bean的初始化之后对Bean进行代理。代理后的Bean会具备增强逻辑。这个实现类就是AbstractAutoProxyCreator,接下来我们就来看下AbstractAutoProxyCreator是如何生成代理Bean的。postProcessAfterInitialization逻辑如下

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            // 获取缓存key
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                // 生成代理bean
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
}

其中wrapIfNecessary方法的作用就是:如果bean有必要被代理则返回代理对象,否则不进行代理。接下来开一下wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 省略一些判断逻辑
    // 这里获取被代理的bean的所有相关拦截器,比如自定义的切面,Transactional注解的拦截器
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 使用CGLIB创建代理对象,很重要
        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;
}

wrapIfNecessary方法中,getAdvicesAndAdvisorsForBean方法是获取拦截器,这里是很关键的逻辑,bean的增强逻辑实际上都是通过一个个拦截器(advisor)来实现的。如果没有获取到拦截器则说明bean不需要被代理——就会返回源对象。否则就会试图通过createProxy方法创建代理对象。

也就是说wrapIfNecessary方法做了两件重要的事情

  1. 获取拦截器specificInterceptors
  2. 如果存在拦截器,则使用拦截器创建代理对象createProxy

获取拦截器

获取拦截器是getAdvicesAndAdvisorsForBean方法实现的。我们来看一下这个方法是如何获取到拦截器的。

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    // 获取合适的拦截器  Advisor就是拦截器
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 获取所有的拦截器
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 过滤掉不满足条件的拦截器
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

getAdvicesAndAdvisorsForBean方法就是要给壳子方法,它通过findEligibleAdvisors方法完成拦截器的查询获取。在findEligibleAdvisors方法中

  1. 通过findCandidateAdvisors获取所有的拦截器
  2. 通过findAdvisorsThatCanApply对第一步的拦截器进行过滤,只留下合适的拦截器。比如方法上没有Transactional注解的时候,事务拦截器就会在次被过滤掉

SpringAOP源码解析

SpringAOP源码解析

至此,就获取到执行切面必须的拦截器了,通过debug可以发现它是一个InstantiationModelAwarePointcutAdvisorImpl类型的Advisor,这里补充一下,在SpringAOP中Interceptor就是Advisor,源码中Interceptor是Advisor的子接口。

补充:Advisor、Advice、Advised、Interceptor、PointCut

Advice:具体的建议,即拦截器的逻辑

Advisor:顾问,它包含了Advice、PointCut。既然是顾问,它总得知道自己要对谁进行建议,顾问就是通过PointCut来定位对象

Advised:过去式,已建议;它可以包揽一个顾问列表。

他们之间的关系如下

SpringAOP源码解析

如果把Bean比作一个人,那么增强逻辑+切入点就相当于是顾问,顾问在指定地方建议你做指定的事。也就符合了Spring中这些命名含义。

生成代理对象

还记得获取拦截器之后,要做什么吗?当然是把拦截器们和bean绑定起来。这个绑定的动作就是为源bean生成代理bean,由代理bean执行拦截器里的增强逻辑。我们接着上文来看创建代理的方法

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);
    }
    // 它是一个ProxyCreatorSupport--AdvisedSupport--Advised
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);


    // 把所有的连接器都转成了Advisor
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // ProxyFactory代理工厂中要保留拦截器对象advisors和源对象targetSource
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // Use original ClassLoader if bean class not locally loaded in overriding class loader
    ClassLoader classLoader = getProxyClassLoader();
    if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
        classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
    }
    return proxyFactory.getProxy(classLoader);
}

这个方法的逻辑相对简单,创建代理工厂,再把拦截器数组转换成为顾问列表,然后把顾问列表交给工厂,由工厂来完成创建代理对象的行为。重要的就是最后一行代码。proxyFactory.getProxy(classLoader);具体创建代理的行为交给工厂来做。工厂只需要具备原材料就行了,这里的原材料就是代理顾问列表advisors

来看一下ProxyFactory#getProxy方法

public Object getProxy(@Nullable ClassLoader classLoader) {
    // createAopProxy()就是CglibAopProxy
    return createAopProxy().getProxy(classLoader);
}
// org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)
public Object getProxy(@Nullable ClassLoader classLoader) {
      Class<?> rootClass = this.advised.getTargetClass();
      Class<?> proxySuperClass = rootClass;
      if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
          proxySuperClass = rootClass.getSuperclass();
          Class<?>[] additionalInterfaces = rootClass.getInterfaces();
          for (Class<?> additionalInterface : additionalInterfaces) {
              this.advised.addInterface(additionalInterface);
          }
      }

      validateClassIfNecessary(proxySuperClass, classLoader);

      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
          enhancer.setClassLoader(classLoader);
          if (classLoader instanceof SmartClassLoader &&
                  ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
              enhancer.setUseCache(false);
          }
      }
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
      // 代理对象的方法调用最后都是回调函数在起作用DynamicAdvisedInterceptor
      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
          types[x] = callbacks[x].getClass();
      }
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
              this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);

      return createProxyClassAndInstance(enhancer, callbacks);
  }
}

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
    enhancer.setInterceptDuringConstruction(false);
    enhancer.setCallbacks(callbacks);
    return (this.constructorArgs != null && this.constructorArgTypes != null ?
            enhancer.create(this.constructorArgTypes, this.constructorArgs) :
            // 创建代理对象
            enhancer.create());
}

createAopProxy()方法简单说一下,它的内部是判断使用JDK动态代理,还是使用CGLIB动态代理,再SpringBoot2.2.x版本中,AOP已全是CGLIB代理,所以就不再详细介绍了。也就是说createAopProxy()方法最终返回的是CGLIB代理工厂。

而CGLIB最后就是通过enhancer.create()来创建代理对象的。我们知道CGLIB的代理对象回调逻辑的关键再Enhancer的setCallbacks方法。这里的callback就是把包装了顾问列表的callback。最终执行逻辑在DynamicAdvisedInterceptor的invoke中。通过下图的继承体系,可以看出DynamicAdvisedInterceptor就是一个Callback。回调逻辑就在DynamicAdvisedInterceptor中了。至此Spring就为AOP产生了一个代理对象,此时容器中的bean不再是CService类型,而是要给CGLIB代理过后的代理类了。

SpringAOP源码解析

代理对象回调

前一小节说到,SpringAOP在BeanPostProcessorpostProcessAfterInitialization方法中生成的代理对象。那代理对象生成以后,回调逻辑是如何执行的呢?换句话说,CService的hello*方法是如何执行的呢?

由于Spring容器中存在的已经不是CService对象,而是CService的代理对象,所以在调用hello*方法的时候,实际上会走进DynamicAdvisedInterceptor的intercept方法中

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
    private final AdvisedSupport advised;

    @Override
    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 {
            // 获取拦截器链,使用了责任链模式+递归
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            Object retVal;
            // 非public方法、toString方法、hashCode方法、equals方法会走该逻辑
            if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = invokeMethod(target, method, argsToUse, methodProxy);
            }
            // 其他方法执行回调逻辑
            else {
                retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
            }
            retVal = processReturnType(proxy, target, method, retVal);
            return retVal;
        }
        // 省略其他代码
    }
}

public Object proceed() throws Throwable {
    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)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // 递归执行
            return proceed();
        }
    }
    else {
        // 拦截器执行拦截逻辑
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}
转载自:https://juejin.cn/post/7294468438391947315
评论
请登录