Spring源码分析之AOP

作者: 一字马胡
转载标志 【2018-01-02】

更新日志

日期 更新内容 备注
2018-01-02 创建分析文档 Spring源码分析系列文章(三)

前言

本文是Spring源码分析系列的第三篇文章,前两篇文章分别分析总结了Spring bean的解析以及bean的加载过程,可以参考下面的链接来了解详细内容:

本文涉及Spring源码中关于AOP相关的内容,AOP(Aspect-OrientedProgramming,面向方面编程)是一种编程思想,是一种面向切面的编程方法,也就是说,AOP是一种横向的编程技术,与面向对象的编程思想不一样,面向对象的编程思想是一种纵向的编程技术,面向对象需要我们做抽象封装,而AOP则希望对应用进行横向扩展,类似记录方法调用日志、方法调用耗时等这样的需求,使用面向对象的思想就不太容易解决,而AOP就是用来解决这类具有“横向”关系的问题的。

在进行AOP源码分析之前,需要了解几个Spring AOP的关键概念:

  • 切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。

  • 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

  • 通知(Advice): 在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。

  • 切入点(Pointcut): 指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,可以通过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否可以被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上

  • 目标对象(Target Object): 包含连接点的对象。也被称作被通知或被代理对象。

  • AOP代理(AOP Proxy): AOP框架创建的对象,包含通知。 在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

  • 织入(Weaving): 组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

在开始进行源码分析之前,先来看一下怎么使用Spring AOP来做一些事情,仅介绍基于注解方式的使用方法,xml配置方式的暂不做介绍。在xml配置文件中需要把aop功能打开,添加下面的配置:


 <aop:aspectj-autoproxy/>

然后就可以在项目中使用Spring AOP了,下面是一个简单的小例子:


package com.hujian.aop;

import com.hujian.star.log.impl.SampleLocalLogServiceImpl;
import com.hujian.star.log.utils.LogUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;

/**
 * Created by hujian on 2017/5/11.
 */
@Aspect
public class ServiceAop implements Serializable {

    @Autowired
    private SampleLocalLogServiceImpl sampleLocalLogHandler = null;

    /**
     * the star
     */
    @Pointcut("execution(* com.hujian.*.services.*..*(..))")
    public void aspect(){
    }

    /**
     * before calling the services
     * for the methods call-time statistic info.
     */
    @Before("aspect()")
    public void BeforeCallServices(JoinPoint joinPoint){
        String dec = joinPoint.getSignature().getDeclaringType().getName();
        String method = joinPoint.getSignature().getName();
        String args = "";
        for( Object arg : joinPoint.getArgs() ){
            args += arg.toString() + " ";
        }
        //do the log,then we can read the record from log to do some statistics
        //such as the call count of the methods
        String log = "[" + LogUtils.getTime() + "]\t" + method + "\t" + args + "\t" + dec + "\n";
        sampleLocalLogHandler.writeLog(log,"aop_info");
    }

}

需要使用@Aspect注解来告诉Spring这是一个AOP类,里面有几个注解需要注意,@Pointcut定义了一个切点,也就是在什么地方切入,@Before注解说明需要在切点之前触发,还有一些其他的注解可以使用,在下面的分析中逐渐说明。

Spring AOP源码分析

在编写了一个简单的Spring AOP应用之后,现在开始来分析Spring AOP的源码实现,本系列文章不会涉及太过细节的内容,只会将主干内容分析总结出来,细节内容将在未来逐步补充学习。另外一点需要说明的是,细节是很重要的,但是又是很花费时间的,在没有理清整体框架思路之前分析细节是很危险的,因为很容易掉进去出不来,特别是对于复杂的框架,如果太过注意细节的话,首先是需要花费大量的时间再抠细节,其次会因为有些细节没有及时搞懂而耽误进度。一个合理的做法是首先将主干打通,然后再根据实际中碰到的问题进行细节分析整体,这样的学习效果才是最好的,效率也是最高的。

本文的分析基于注解,而xml配置相关的内容将不再本文的分析范围之内。上面的例子中已经提到,我们需要关注的是@Aspect这个注解。现在开始来进行AOP相关的源码分析。

在上面的例子中,xml中配置了 <aop:aspectj-autoproxy/>来开启aop功能,而 <aop:aspectj-autoproxy/>似乎是一个自定义标签,不想bean等默认标签,而在Spring中,每一个自定义标签都会对应一个handler来进行处理,全局搜索 aspectj-autoproxy就会找到这个handler,也就是AopNamespaceHandler,在init方法内部,找到aspectj-autoproxy对应的parser是AspectJAutoProxyBeanDefinitionParser,所以,本文对Spring AOP源码的分析从AspectJAutoProxyBeanDefinitionParser这个类开始。关于自定义标签的解析,可以参考文章:浅析Spring自定义标签的使用

AspectJAutoProxyBeanDefinitionParser类的parse方法是标签解析的入口,主要关注AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element)这行代码,跟踪进去可以看到下面的代码:


    public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {

        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
        registerComponentIfNecessary(beanDefinition, parserContext);
    }

主要关注registerAspectJAnnotationAutoProxyCreatorIfNecessary这个方法调用,进去之后发现了一个关键的类AnnotationAwareAspectJAutoProxyCreator,这个类也是本文源码分析的重点。下面首先展示了AnnotationAwareAspectJAutoProxyCreator类的类图:

可以看到AnnotationAwareAspectJAutoProxyCreator 继承了BeanPostProcessor,所以可以知道,Spring在加载该bean的时候,在实例化前会调用postProcessAfterInitialization方法,所以分析的入口是AnnotationAwareAspectJAutoProxyCreator的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这个方法的调用。


    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 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;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

在真正开始进行流程内容之前会做一些判断,比如是否是一个基础设施bean,或者是否被子类设定可以skip等等,关键的代码是对方法getAdvicesAndAdvisorsForBean的调用,调用getAdvicesAndAdvisorsForBean方法可以获取到增强方法,获取到增强方法之后就可以进行代理了。现在先来分析getAdvicesAndAdvisorsForBean方法的具体实现过程。跟踪该方法几步之后可以发现下面的关键方法:


    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;
    }

findCandidateAdvisors方法的功能是获取到所有的增强,findAdvisorsThatCanApply方法的功能是获取可以适用于该bean的增强。下面先来分析findCandidateAdvisors的具体流程。因为分析是基于注解分析的,所以会走到下面的方法中:


    protected List<Advisor> findCandidateAdvisors() {
        // Add all the Spring advisors found according to superclass rules.
        List<Advisor> advisors = super.findCandidateAdvisors();
        // Build Advisors for all AspectJ aspects in the bean factory.
        if (this.aspectJAdvisorsBuilder != null) {
            advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        }
        return advisors;
    }

super方法的调用是为了获取xml中的aop配置,而该方法中需要关注的内容时this.aspectJAdvisorsBuilder.buildAspectJAdvisors这一行代码。该方法实现的功能为下面几个个:

  • 获取所有的beanName,所有在BeanFactory中注册的bean都会被扫描到
  • 遍历所有的beanName,然后将那些声明为Aspect注解的beanName挑选出来
  • 开始解析这些挑选出来的bean,提取增强内容
  • 将提取的内容缓存起来

下面分别分析每一个步骤是怎么实现的,首先,获取所有的beanName,可以通过下面的代码实现:


String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                            this.beanFactory, Object.class, true, false);

获取到了所有的bean之后,遍历这些bean,然后逐个进行判断,首先获取bean的type,然后判断是不是声明为Aspect注解的bean,如果是的话继续解析,不是的话就跳过,下面来看一下是如何判断一个bean是不是声明为Aspect注解的beanName的:


    public boolean isAspect(Class<?> clazz) {
        return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
    }

    private boolean hasAspectAnnotation(Class<?> clazz) {
        return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
    }   

    private static <A extends Annotation> A findAnnotation(
            Class<?> clazz, @Nullable Class<A> annotationType, boolean synthesize) {

        Assert.notNull(clazz, "Class must not be null");
        if (annotationType == null) {
            return null;
        }

        AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType);
        A result = (A) findAnnotationCache.get(cacheKey);
        if (result == null) {
            result = findAnnotation(clazz, annotationType, new HashSet<>());
            if (result != null && synthesize) {
                result = synthesizeAnnotation(result, clazz);
                findAnnotationCache.put(cacheKey, result);
            }
        }
        return result;
    }

接着开始最为关键的一步,就是提取增强器,相关的方法是this.advisorFactory.getAdvisors,下面来分析一下是如何做到提取增强器的。


    public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
        validate(aspectClass);

        // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
        // so that it will only instantiate once.
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

        List<Advisor> advisors = new LinkedList<>();
        for (Method method : getAdvisorMethods(aspectClass)) {
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        // If it's a per target aspect, emit the dummy instantiating aspect.
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }

        // Find introduction fields.
        for (Field field : aspectClass.getDeclaredFields()) {
            Advisor advisor = getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
    }

主要关注其中的getAdvisor方法调用:


    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
            int declarationOrderInAspect, String aspectName) {

        validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

        AspectJExpressionPointcut expressionPointcut = getPointcut(
                candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
        if (expressionPointcut == null) {
            return null;
        }

        return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
                this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
    }

然后关注切点信息的获取方法getPointcut:


    private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }

        AspectJExpressionPointcut ajexp =
                new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
        ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
        if (this.beanFactory != null) {
            ajexp.setBeanFactory(this.beanFactory);
        }
        return ajexp;
    }

首先调用findAspectJAnnotationOnMethod方法获取了所有aop感兴趣的注解信息,可以看下面的具体的代码:


    protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
        Class<?>[] classesToLookFor = new Class<?>[] {
                Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
        for (Class<?> c : classesToLookFor) {
            AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
            if (foundAnnotation != null) {
                return foundAnnotation;
            }
        }
        return null;
    }

这里面的findAnnotation方法是一个需要关注的方法:


    private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
        A result = AnnotationUtils.findAnnotation(method, toLookFor);
        if (result != null) {
            return new AspectJAnnotation<>(result);
        }
        else {
            return null;
        }
    }

重点在于return new AspectJAnnotation<>(result)这一行代码:


        public AspectJAnnotation(A annotation) {
            this.annotation = annotation;
            this.annotationType = determineAnnotationType(annotation);
            // We know these methods exist with the same name on each object,
            // but need to invoke them reflectively as there isn't a common interface.
            try {
                this.pointcutExpression = resolveExpression(annotation);
                this.argumentNames = (String) annotation.getClass().getMethod("argNames").invoke(annotation);
            }
            catch (Exception ex) {
                throw new IllegalArgumentException(annotation + " cannot be an AspectJ annotation", ex);
            }
        }

        private String resolveExpression(A annotation) throws Exception {
            for (String methodName : EXPRESSION_PROPERTIES) {
                Method method;
                try {
                    method = annotation.getClass().getDeclaredMethod(methodName);
                }
                catch (NoSuchMethodException ex) {
                    method = null;
                }
                if (method != null) {
                    String candidate = (String) method.invoke(annotation);
                    if (StringUtils.hasText(candidate)) {
                        return candidate;
                    }
                }
            }
            throw new IllegalStateException("Failed to resolve expression: " + annotation);
        }

到此为止,所有aop相关的注解都可以获取到了,下面回过头来看getPointcut方法接下来的内容,可以看到new出来了一个AspectJExpressionPointcut对象,并且将其expression属性设置为了上面解析好的PointcutExpression,以及设置BeanFactory并且返回。现在可以回到getAdvisor方法,来看接下来的内容,在getAdvisor方法中,调用getPointcut方法获取到信息之后,getAdvisor方法会返回一个InstantiationModelAwarePointcutAdvisorImpl对象,下面的图片展示了InstantiationModelAwarePointcutAdvisorImpl类的构造函数的细节,看起来就是将一些属性保存起来,然后最后有一个非常关键的方法调用:instantiateAdvice。

下面来分析instantiateAdvice方法的具体实现细节。


    private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
        Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
                this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
        return (advice != null ? advice : EMPTY_ADVICE);
    }

继续跟踪下去,就会走到ReflectiveAspectJAdvisorFactory类的getAdvice方法中去,该方法中会根据aspectJAnnotation的类型来返回相应的Advice,细节入下图所示:

比如对于@Before注解,会生成一个AspectJMethodBeforeAdvice类型的Advice,下图展示了AspectJMethodBeforeAdvice类的实现细节。

主要关注before方法,在before方法中调用了invokeAdviceMethod方法,下面来分析一下invokeAdviceMethod方法的实现细节:


    protected Object invokeAdviceMethod(
            @Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
            throws Throwable {

        return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
    }

接着又调用了invokeAdviceMethodWithGivenArgs方法,下面是invokeAdviceMethodWithGivenArgs方法的实现内容:


    protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
        Object[] actualArgs = args;
        if (this.aspectJAdviceMethod.getParameterCount() == 0) {
            actualArgs = null;
        }
        try {
            ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
            // TODO AopUtils.invokeJoinpointUsingReflection
            return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
        }
        catch (IllegalArgumentException ex) {
            throw new AopInvocationException("Mismatch on arguments to advice method [" +
                    this.aspectJAdviceMethod + "]; pointcut expression [" +
                    this.pointcut.getPointcutExpression() + "]", ex);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }

在该方法中,触发了实际设置的增强方法。这里面有一个细节需要我们分析一下,这里面的args是怎么来的?这也就牵扯到Spring是如何解析aop表达式的问题,下面来分析一下Spring解析aop的流程。在getAdvice方法的末尾,有一个关键的方法调用:springAdvice.calculateArgumentBindings(),下面来分析一下这个方法的具体实现细节:


    public synchronized final void calculateArgumentBindings() {
        // The simple case... nothing to bind.
        if (this.argumentsIntrospected || this.parameterTypes.length == 0) {
            return;
        }

        int numUnboundArgs = this.parameterTypes.length;
        Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
        if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) {
            numUnboundArgs--;
        }
        else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
            numUnboundArgs--;
        }

        if (numUnboundArgs > 0) {
            // need to bind arguments by name as returned from the pointcut match
            bindArgumentsByName(numUnboundArgs);
        }

        this.argumentsIntrospected = true;
    }

这其中有一个关键的方法调用:bindArgumentsByName(numUnboundArgs):


    private void bindArgumentsByName(int numArgumentsExpectingToBind) {
        if (this.argumentNames == null) {
            this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
        }
        if (this.argumentNames != null) {
            // We have been able to determine the arg names.
            bindExplicitArguments(numArgumentsExpectingToBind);
        }
        else {
            throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
                    "requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
                    "the argument names were not specified and could not be discovered.");
        }
    }

然后主要看getParameterNames这个方法调用,具体的实现细节可以参考AspectJAdviceParameterNameDiscoverer类中的getParameterNames的具体实现,详细的细节就不再次分析了。

在解析完所有的增强之后,就需要为当前bean挑选合适的增强器了,因为所有的增强器不一定都能匹配到当前bean,而这个挑选匹配的增强器的工作由findAdvisorsThatCanApply方法完成,下面来分析一下这个方法的具体内容:


    protected List<Advisor> findAdvisorsThatCanApply(
            List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

        ProxyCreationContext.setCurrentProxiedBeanName(beanName);
        try {
            return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
        }
        finally {
            ProxyCreationContext.setCurrentProxiedBeanName(null);
        }
    }

主要关注 AopUtils.findAdvisorsThatCanApply这一句:


    public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List<Advisor> eligibleAdvisors = new LinkedList<>();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                eligibleAdvisors.add(candidate);
            }
        }
        boolean hasIntroductions = !eligibleAdvisors.isEmpty();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor) {
                // already processed
                continue;
            }
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

主要看canApply这个方法的实现:


    public static boolean canApply(Advisor advisor, Class<?> targetClass) {
        return canApply(advisor, targetClass, false);
    }

    public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
        if (advisor instanceof IntroductionAdvisor) {
            return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
        }
        else if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pca = (PointcutAdvisor) advisor;
            return canApply(pca.getPointcut(), targetClass, hasIntroductions);
        }
        else {
            // It doesn't have a pointcut so we assume it applies.
            return true;
        }
    }
    

具体的匹配判断过程可以看下面的代码:


    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        }

        MethodMatcher methodMatcher = pc.getMethodMatcher();
        if (methodMatcher == MethodMatcher.TRUE) {
            // No need to iterate the methods if we're matching any method anyway...
            return true;
        }

        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
            introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
        }

        Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        classes.add(targetClass);
        for (Class<?> clazz : classes) {
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
            for (Method method : methods) {
                if ((introductionAwareMethodMatcher != null &&
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }

        return false;
    }

到此为止,我们获取了所有bean对应的增强器,下面就可以开始创建代理了,而创建代理则通过createProxy方法来进行,下面来开始分析一下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();
        proxyFactory.copyFrom(this);

        if (!proxyFactory.isProxyTargetClass()) {
            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);
        }

        return proxyFactory.getProxy(getProxyClassLoader());
    }

首先new了一个ProxyFactory对象,然后调用了ProxyFactory对象的copyFrom方法,该方法实现的功能是从当前对象中获取一些属性,具体实现细节如下:


    public void copyFrom(ProxyConfig other) {
        Assert.notNull(other, "Other ProxyConfig object must not be null");
        this.proxyTargetClass = other.proxyTargetClass;
        this.optimize = other.optimize;
        this.exposeProxy = other.exposeProxy;
        this.frozen = other.frozen;
        this.opaque = other.opaque;
    }

接着,判断对于给定的bean是否应该使用targetClass而不是它的接口代理,其中,shouldProxyTargetClass方法用于判断是否应该使用targetClass类而不是接口来进行代理,下面来看具体的实现细节:


    protected boolean shouldProxyTargetClass(Class<?> beanClass, @Nullable String beanName) {
        return (this.beanFactory instanceof ConfigurableListableBeanFactory &&
                AutoProxyUtils.shouldProxyTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName));
    }


    public static boolean shouldProxyTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
        if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
            BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
            return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
        }
        return false;
    }

如果使用接口代理的话,会执行evaluateProxyInterfaces方法,具体细节如下:


    protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
        Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
        boolean hasReasonableProxyInterface = false;
        for (Class<?> ifc : targetInterfaces) {
            if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
                    ifc.getMethods().length > 0) {
                hasReasonableProxyInterface = true;
                break;
            }
        }
        if (hasReasonableProxyInterface) {
            // Must allow for introductions; can't just set interfaces to the target's interfaces only.
            for (Class<?> ifc : targetInterfaces) {
                proxyFactory.addInterface(ifc);
            }
        }
        else {
            proxyFactory.setProxyTargetClass(true);
        }
    }

接着回到createProxy方法中,看剩下部分的流程,接着是一个重要的方法buildAdvisors,这个方法的功能是将该bean的所有Advisor以及拦截器统一封装成Advisor,并设置到proxyFactory中。具体的包装细节可以参考下面的代码:


    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            // So well-known it doesn't even need an adapter.
            return new DefaultPointcutAdvisor(advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // Check that it is supported.
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }

接着关键部分是调用proxyFactory.getProxy来获取代理对象。


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

而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);
        }
    }

实现代理有两种可选的方式,jdk动态代理和cglib代理。具体的选择过程策略如下:

  • 如果目标对象实现了接口,则默认选择使用jdk动态代理
  • 如果目标对象没有实现接口,那么必须使用cglib来实现代理

下面先来看jdk动态代理的实现过程。


    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动态代理的内容,可以参考Java静态代理和动态代理。主要看Proxy.newProxyInstance的第三个参数,它是InvocationHandler的实现类,主要看InvocationHandler类的invoke方法。主要看下面的代码:

主要工作就是创建了一个拦截器链条,然后逐个调用,关键代码如下:


public Object proceed() throws Throwable {
        //  We start with an index of -1 and increment early.
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        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;
            if (dm.methodMatcher.matches(this.method, this.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);
        }
    }

需要调用完所有的拦截器才会执行切点方法,这里面还是有很多细节,待理清楚了再补充说明。关于使用cglib进行代理的实现可以参考CglibAopProxy#getProxy的具体实现,本文不再进行分析总结。

至此,本文的分析内容就结束了,分析的内容非常粗略,但是可以从中看出Spring AOP实现的原理,以及一些细节,这其中还有大量的细节需要学习补充,本文作为Spring源码分析的第三篇文章,主要内容时分析Spring AOP的实现,关于如何使用Spring AOP以及更为细节的一些内容将在未来分析总结,然后再进行补充。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,699评论 2 22
  • AOP核心概念 横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点切面(aspect):类...
    lijiaccy阅读 323评论 0 0
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,736评论 6 342
  • 父亲离开我们28年了,为了避免母亲难过,伤感,我们姐妹很少在母亲跟前提起父亲,只是,偶尔也会在母亲自己说起父亲的时...
    十六夜蔷薇1阅读 266评论 0 0