Spring源码分析之AOP

AOP(Aspect Oriented Programing),即面向切面编程。它的主要思想是将分散在各个业务逻辑中的代码通过横向切割的方式抽取到一个独立的模块中。通常可以用于性能监测,访问控制,事务管理和日志记录等。

1. Spring AOP的使用

Spring AOP同时支持XML和注解两种方式进行配置。

1.1 基于注解方式

  1. 首先Spring文件中启用Spring注解支持和AspectJ注解支持。


 

  1. 下面是切面的定义:
@Component
@Aspect
public class LogServiceAspect {

    private static final Log log = LogFactory.getLog(LogServiceAspect.class);
    
    //切点
    @Pointcut("execution(* io.cheergoivan.github.aop.service..*(..))")
    public void servicePointcut(){    }
    
    //前置Advice
    @Before("servicePointcut()")
    public void before(JoinPoint joinPoint){
        log.info("Before " + joinPoint);
    }
    
    //后置Advice
    @After("servicePointcut()")
    public void after(JoinPoint joinPoint){
        log.info("After " + joinPoint);
    }
    
    //环绕Advice
    @Around("servicePointcut()")
    public void around(JoinPoint joinPoint){
        log.info("Before around " + joinPoint);
        ((ProceedingJoinPoint) joinPoint).proceed();
        log.info("After around " + joinPoint);
    }
    
    //后置返回Advice
    @AfterReturning("servicePointcut()")
    public void afterReturn(JoinPoint joinPoint){
        log.info("AfterReturn " + joinPoint);
    }
    
    //抛出异常后Advice
    @AfterThrowing(pointcut="servicePointcut()", throwing="e")
    public void afterThrow(JoinPoint joinPoint, Exception e){
        log.info("AfterThrow " + joinPoint + " " + ex.getMessage());
    }
}

JoinPoint保存着被拦截的方法。而@Around用于配置环绕执行,这意味着我们可以在通过@Around实现@Before和@After的效果。

1.2 基于XML方式

下面是基于XML的配置方式:




    
    
        
        
        
        
        
        
        
    

以上的两种AOP配置方式是等价的。

2. Spring AOP基本概念

下面是Spring AOP中的基本概念:

  • Aspect:切面。包含了切点(Pointcut)定义和通知(Advice)定义的一个模块。用于定义程序何时织入以及具体织入代码的模块。
  • JoinPoint:连接点。 程序执行时的某个特定的点。例如方法的执行或者处理一个exception等。在Spring中,JoinPoint特指一个方法的执行。因此,Spring AOP只支持方法级别的织入。
  • Advice:通知。当到达JoinPoint时,额外要执行的行为。通知又分为前置通知(@Before),后置通知(@After),环绕通知(@Around)等等。
  • Pointcut:切点。切点是一个断言,用于设置目标JoinPoint。定义Advice时需要指定Pointcut,从而当执行到目标JoinPoint时,就会执行这个Advice。
  • Target object:目标对象。即被Spring AOP代理的对象。
  • AOP proxy:AOP 代理。Spring AOP创建的已经被织入了Advice的代理(proxy)对象。
  • Weaving:织入。将切面定义的代码在目标对象中生效的过程被称为织入。织入的本质是创建一个和目标对象具有相同接口的代理对象。织入又分为编译期织入,加载期织入和运行期织入。Spring AOP使用的是运行期织入。
Spring源码分析之AOP_第1张图片
Spring AOP.png

3. Spring AOP源码分析

3.1

作为AOP的入口,是为了开启注解支持,从而我们能够通过诸如@Aspect,@Pointcut等注解定义切面,切点等信息。下面是它的源码,位于AopNamespaceHandler#init方法中:

registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

AspectJAutoProxyBeanDefinitionParser 实现了BeanDefinitionParser接口,因此,Spring容器在启动时会调用其parse方法。下面是AspectJAutoProxyBeanDefinitionParser#parse方法的实现:

public BeanDefinition parse(Element element, ParserContext parserContext) {
        AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        extendBeanDefinition(element, parserContext);
        return null;
}

核心代码为AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);,它的目的是注册一个AnnotationAwareAspectJAutoProxyCreator的Bean。

AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,因此Spring AOP创建代理的过程是在Spring容器创建Bean实例时进行的。

配置等价的一个注解是@EnableAspectJAutoProxy@EnableAspectJAutoProxy注解调用了@Import(AspectJAutoProxyRegistrar.class)AspectJAutoProxyRegistrar类实现了ImportBeanDefinitionRegistrar接口,主要目的是向Spring容器中注入AnnotationAwareAspectJAutoProxyCreator

3.2 AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,创建proxy的过程主要位于postProcessAfterInitialization方法中,下面是其实现:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
}

其中wrapIfNecessary承担了创建proxy的职责,它的主要实现如下:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    //...
    // Create proxy if we have advice.
    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;
    }
   //...
}

wrapIfNecessary方法创建代理的过程主要分为两步:
1. getAdvicesAndAdvisorsForBean获得所有支持目标Bean的Advisor。
2. createProxy读取这些Advisor定义,通过CglibProxy或DynamicJdkProxy创建proxy。

下面我们将阐述Advisor的概念。

3.3 Advisor

Advisor是一个Advice的容器,下面是其接口定义:

public interface Advisor {

    Advice EMPTY_ADVICE = new Advice() {};

    Advice getAdvice();  

    //...
}

Advice本身是一个标记接口,表明这是一个Advice。AbstractAspectJAdvice是它的主要抽象实现类。继承了AbstractAspectJAdvice的子类包括AspectJAfterAdviceAspectJMethodBeforeAdviceAspectJAfterReturningAdvice等等。

AbstractAspectJAdvice的主要数据结构如下:

private final Class declaringClass;

private final String methodName;

private final Class[] parameterTypes;

protected transient Method aspectJAdviceMethod;

private final AspectJExpressionPointcut pointcut;

因此AspectJMethodBeforeAdvice封装了Advice调用时所需要的一些数据,例如具体的Method,具体的Potincut等等。

MethodInterceptor接口则负责了具体的Advice调用逻辑,它的接口定义如下:

public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation invocation) throws Throwable;
}

AspectJAfterAdvice实现了MethodInterceptor接口,它的实现如下:

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    }
    finally {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

我们可以看到,AspectJAfterAdviceinvoke(MethodInvocation mi) 方法首先会继续执行MethodInvocation#proceed,最后再通过invokeAdviceMethod调用Advice的实际增强代码。

3.4 getAdvicesAndAdvisorsForBean

getAdvicesAndAdvisorsForBean获取符合该Bean的Advisor的过程如下:
1. 调用AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors获取Spring容器中所有的Advisor。
2. AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors会调用BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors方法。
3. BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors方法。读取cache,如果cache不为空,则直接返回。如果cache为空,则遍历所有的Bean,对每一个带有@Aspect注解的Bean,通过ReflectiveAspectJAdvisorFactory.getAdvisors来解析对应的Advisor。此方法会依次解析该Bean方法上的Advice注解,包括:@Around, Before, @After, @AfterReturning, @AfterThrowing等,然后封装成Advisor对象排序并返回。
4. 至此,AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors方法结束,此时返回了Spring容器内的所有Advisor。
5. 调用AbstractAdvisorAutoProxyCreator#List findAdvisorsThatCanApply( List candidateAdvisors, Class beanClass, String beanName)方法过滤出符合当前Bean的Advisor,然年返回。

Advisor的实现类中包含了Pointcut实例,判断一个Pointcut是否符合一个Bean的代码如下:

boolean canApply(Pointcut pc, Class targetClass) {
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    for (Class clazz : classes) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if (methodMatcher.matches(method, targetClass)) {
            return true;
        }
        }
    }
    return false;
}

3.5 createProxy

createProxy的主要实现如下:

`protected Object createProxy(Class beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    //....
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);
    return proxyFactory.getProxy(getProxyClassLoader());
}

我们可以看到,createProxy方法调用了ProxyFactory.getProxy来创建代理对象。下面是ProxyFactory.getProxy的实现:

public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}

createAopProxy()方法则是创建了一个AopProxy对象,然后通过这个AopProxy来创建真正的代理对象。下面是createAopProxy()方法的实现:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        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.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

可以看到,默认情况下使用JdkDynamicAopProxy,除非目标对象没有接口并且在将proxyTargetClass属性设置为true。

下面是JdkDynamicAopProxy.getProxy方法的实现:

public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

可以看到,本质上是通过jdk的动态代理即Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)来实现的,因此JdkDynamicAopProxy实现了InvocationHandler接口,下面是它的实现:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //...
    Class targetClass = (target != null ? target.getClass() : null);
    List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    if (chain.isEmpty()) {
        Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
        retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
    }else {
        // We need to create a method invocation...
        invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
        // Proceed to the joinpoint through the interceptor chain.
        retVal = invocation.proceed();
    }
    //...
    return retVal;
}

1. getInterceptorsAndDynamicInterceptionAdvice目的是获取符合该Bean和Method的MethodInterceptor集合,它本质上是获取所有符合该Bean和Method的Advisor,然后转化为MethodInterceptor(如果Advisor实现了MethodInterceptor接口,则直接返回,否则调用AdvisorAdapter.getInterceptor(advisor)进行转化)。
2. 包装一个ReflectiveMethodInvocation对象,它内部维护了一个MethodInterceptor链,依次调用这个MethodInterceptor链的invoke(MethodInvocation invocation)方法,最终返回这个被MethodInterceptor链处理过的实例。

下面是ReflectiveMethodInvocation.proceed的实现:

public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 如果所有的增强都执行完成,则执行增强方法
        return this.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 this.proceed();
        }
    } else {
        // 普通拦截器:直接应用拦截方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

一个常见的Before Advice的MethodInterceptor的实现如下:

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        //这个invoke方法是拦截器的回调方法,会在代理对象的方法被调用的时候出发回调
        return mi.proceed();
    }
}

4. 总结

AOP的流程如下:

1. 通过@EnableAspectJAutoProxy注册了AnnotationAwareAspectJAutoProxyCreator,这是一个BeanPostProcessor,因此会在Spring容器创建Bean时调用其postProcessAfterInitialization方法。
2. postProcessAfterInitialization方法首先会获得符合该Bean定义的所有Advisor,然后调用createProxy方法。
3. createProxy方法构造一个ProxyFactory对象,并传入上面的Advisor List。然后根据配置选择创建一个AopProxy对象(JdkDynamicAopProxyCglibAopProxy),并调用AopProxy.getProxy创建代理对象。
4. JdkDynamicAopProxygetProxy方法实际上依赖了JDK的动态代理即Proxy.newInstance,同时JdkDynamicAopProxy实现了InvocationHandler负责实现proxy回调函数invoke(Object proxy, Method method, Object[] args),此方法会遍历这个Bean的MethodInterceptor链,然后判断是否match当前Method,如果match则调用这个MethodInterceptor,否则继续试探下一个MethodInterceptor,直到遍历MethodInterceptor链完成。

参考文章

  • https://www.zhenchao.org/2019/01/11/spring/spring-aop-mechanism/
  • https://www.jianshu.com/p/305853067f52

你可能感兴趣的:(Spring源码分析之AOP)