Spring源码分析之AOP

作者: 一字马胡 | 来源:发表于2018-01-02 23:08 被阅读370次

    作者: 一字马胡
    转载标志 【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以及更为细节的一些内容将在未来分析总结,然后再进行补充。

    相关文章

      网友评论

        本文标题:Spring源码分析之AOP

        本文链接:https://www.haomeiwen.com/subject/hxdcnxtx.html