Spring5源码(35)-SpringAop创建代理(一)

简介: Spring5源码(35)-SpringAop创建代理(一)


在前面的两节,通过分析shouldSkip方法,已经完成了SpringAOP中增强(切面)的创建,并将获取到的切面进行缓存,接下来继续分析SpringAOP创建代理的过程。即AbstractAutoProxyCreator类的postProcessAfterInitialization方法。

/**
 * 如果bean被子类标识为要代理的bean,则使用配置的拦截器创建代理。
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 为beanName和beanClass构建缓存key
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // 包装bean
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

创建代理过程发生在wrapIfNecessary方法里,打开该方法:

/**
 * 如果需要则包装该bean,例如该bean可以被代理
 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
 * @param bean the raw bean instance
 * @param beanName the name of the bean
 * @param cacheKey the cache key for metadata access
 * @return a proxy wrapping the bean, or the raw bean instance as-is
 */
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 1、如果已经处理过或者不需要创建代理,则返回
    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;
    }
    // 2、创建代理
    // 2.1 根据指定的bean获取所有的适合该bean的增强
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        // 2.2 为指定bean创建代理
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    // 3、缓存
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

创建代理的过程发生在第二步,先得到适合当前bean的增强,然后再根据得到的增强创建代理。下面逐个分析:

1. 根据指定的bean获取所有的适合该bean的增强流程简析

/**
 * 获取指定bean的增强
 * @param beanClass the class of the bean to advise
 * @param beanName the name of the bean
 * @param targetSource
 * @return
 */
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    // 如果获取到的增强是个空的集合,则返回DO_NOT_PROXY-->空数组
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    // 将获取到的增强转换为数组并返回
    return advisors.toArray();
}

/**
 * 为当前bean获取所有需要自动代理的增强
 * Find all eligible Advisors for auto-proxying this class.
 * @param beanClass the clazz to find advisors for
 * @param beanName the name of the currently proxied bean
 * @return the empty List, not {@code null},
 * if there are no pointcuts or interceptors
 * @see #findCandidateAdvisors
 * @see #sortAdvisors
 * @see #extendAdvisors
 */
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 1、查找所有候选增强
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 2、从所有增强集合中查找适合当前bean的增强
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 3、在eligibleAdvisors集合首位加入ExposeInvocationInterceptor增强
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

流程非常的简单,先的到所有的增强,再从所有增强中找到适合当前bean的增强。其中获取所有增强的过程,已经在前两个章节分析过,这里不再赘述,来看Spring是如何从所有增强中找到适合当前bean的增强的。

3. 从增强集合中找到适合当前bean的增强

/**
 * 从给定的增强中找出可以应用到当前指定bean的增强
 * Search the given candidate Advisors to find all Advisors that
 * can apply to the specified bean.
 * @param candidateAdvisors the candidate Advisors
 * @param beanClass the target's bean class
 * @param beanName the target's bean name
 * @return the List of applicable Advisors
 * @see ProxyCreationContext#getCurrentProxiedBeanName()
 */
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) {
    // 1、候选增强为空,直接放回
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    // 2、处理引介增强
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    // 2、处理普通增强
    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方法判断,并将符合条件的增强加入到Advisor集合中返回即可。那么接下来就该分析canApply方法了。

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    // 1、处理引介增强
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    // 2、处理普通增强
    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) {
    // 1、判断类是否匹配,如类一级别不匹配,直接返回false
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }
    // 2、判断如果当前Advisor所指代的方法的切点表达式如果是对任意方法都放行,直接返回true
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        // No need to iterate the methods if we're matching any method anyway...
        return true;
    }
    // 3、尝试将methodMatcher转换为IntroductionAwareMethodMatcher,以提高匹配效率
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }
    // 4、获取代理目标的所有接口和实现类
    Set<Class<?>> classes = new LinkedHashSet<>();
    if (!Proxy.isProxyClass(targetClass)) {
        classes.add(ClassUtils.getUserClass(targetClass));
    }
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    // 5、循环代理目标的所有接口和实现类的所有方法并调用matches方法做匹配判断
    for (Class<?> clazz : classes) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if (introductionAwareMethodMatcher != null ?
                    // 如果上一步得到的introductionAwareMethodMatcher对象不为空,则使用该对象的matches匹配
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    // 否则使用Pointcut的methodMatcher对象做匹配
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }
    return false;
}

该方法的主要过程在注释里已经分析的很清楚,关键看一下方法匹配过程的处理。

introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)methodMatcher.matches(method, targetClass),因为后者需要根据具体的切点来获取MethodMatcher,所以这里我们只分析一下前者的判断过程。

关于IntroductionAwareMethodMatcher其最主要的实现类就是AspectJExpressionPointcut,来看其具体的匹配过程。

public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
    // 1.检查切点表达式,并缓存
    obtainPointcutExpression();
    // 2.获取ShadowMatch对象并缓存 shadowMatch不知道怎么翻译比较好,姑且叫模糊匹配吧
    ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
    // Special handling for this, target, @this, @target, @annotation
    // in Spring - we can optimize since we know we have exactly this class,
    // and there will never be matching subclass at runtime.
    // 3.永远匹配,如果切入点表达式将匹配 此模糊匹配 的任何连接点(例如,对给定方法的任何调用),则为true。
    if (shadowMatch.alwaysMatches()) {
        return true;
    }
    // 4.永远不匹配,如果切入点表达式永远不能匹配此 模糊匹配 的任何连接点(例如,切入点将永远不会匹配对给定方法的调用),则为true。
    else if (shadowMatch.neverMatches()) {
        return false;
    }
    // 其他匹配:
    else {
        // the maybe case
        if (hasIntroductions) {
            return true;
        }
        // A match test returned maybe - if there are any subtype sensitive variables
        // involved in the test (this, target, at_this, at_target, at_annotation) then
        // we say this is not a match as in Spring there will never be a different
        // runtime subtype.
        RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
        return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
    }
}

private ShadowMatch getTargetShadowMatch(Method method, Class<?> targetClass) {
    // 1.从目标类中找到与method最匹配的方法
    Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    // 如果目标类是一个接口(例如:引介增强)
    // 尝试为继承的方法构建最特定的接口,也可以考虑子接口匹配,特别是代理类.
    // 注意:AspectJ只考虑Method.getDeclaringClass()
    if (targetMethod.getDeclaringClass().isInterface()) {
        // Try to build the most specific interface possible for inherited methods to be
        // considered for sub-interface matches as well, in particular for proxy classes.
        // Note: AspectJ is only going to take Method.getDeclaringClass() into account.
        // 返回给定类的所有接口,包括由超类实现的接口.
        Set<Class<?>> ifcs = ClassUtils.getAllInterfacesForClassAsSet(targetClass);
        if (ifcs.size() > 1) {
            try {
                Class<?> compositeInterface = ClassUtils.createCompositeInterface(
                        ClassUtils.toClassArray(ifcs),
                        targetClass.getClassLoader());
                targetMethod = ClassUtils.getMostSpecificMethod(targetMethod, compositeInterface);
            }
            catch (IllegalArgumentException ex) {
                // Implemented interfaces probably expose conflicting method signatures...
                // Proceed with original target method.
            }
        }
    }
    // 返回方法匹配结果
    return getShadowMatch(targetMethod, method);
}

private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
    // Avoid lock contention for known Methods through concurrent access...
    ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
    if (shadowMatch == null) {
        synchronized (this.shadowMatchCache) {
            // Not found - now check again with full lock...
            PointcutExpression fallbackExpression = null;
            shadowMatch = this.shadowMatchCache.get(targetMethod);
            if (shadowMatch == null) {
                Method methodToMatch = targetMethod;
                try {
                    try {
                        // 获取切点表达式,并做匹配判断,结果保存到ShadowMatch对象中
                        shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
                    }
                    catch (ReflectionWorldException ex) {
                        // Failed to introspect target method, probably because it has been loaded
                        // in a special ClassLoader. Let's try the declaring ClassLoader instead...
                        // 如果匹配失败,可能是因为使用了特殊的类加载器,则尝试使用该特殊的类加载器替换掉默认的类加载器
                        try {
                            // 尝试通过目标类的类加载器获取切点表达式
                            fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
                            if (fallbackExpression != null) {
                                // 再次尝试方法匹配判断
                                shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
                            }
                        }
                        catch (ReflectionWorldException ex2) {
                            fallbackExpression = null;
                        }
                    }
                    // Proxy.isProxyClass-->当且仅当使用getProxyClass方法或newProxyInstance方法将指定的类动态生成为代理类时,方法才返回true。
                    if (targetMethod != originalMethod && (shadowMatch == null ||
                            (shadowMatch.neverMatches() && Proxy.isProxyClass(targetMethod.getDeclaringClass())))) {
                        // Fall back to the plain original method in case of no resolvable match or a
                        // negative match on a proxy class (which doesn't carry any annotations on its
                        // redeclared methods).
                        //在代理类上没有可解析匹配或负匹配(代理类对其重新声明的方法不带任何注释)的情况下,返回到普通的原始方法。
                        methodToMatch = originalMethod;
                        try {
                            shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
                        }
                        catch (ReflectionWorldException ex) {
                            // Could neither introspect the target class nor the proxy class ->
                            // let's try the original method's declaring class before we give up...
                            // 如果原始方法还是无法做出匹配,那么尝试使用原始方法的类,再次去获取切点表达式,并使用该表达式去匹配
                            try {
                                fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
                                if (fallbackExpression != null) {
                                    shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
                                }
                            }
                            catch (ReflectionWorldException ex2) {
                                fallbackExpression = null;
                            }
                        }
                    }
                }
                catch (Throwable ex) {
                    // Possibly AspectJ 1.8.10 encountering an invalid signature
                    logger.debug("PointcutExpression matching rejected target method", ex);
                    fallbackExpression = null;
                }
                // 如果没有得到匹配结果,则默认封装不匹配到ShadowMatchImpl
                if (shadowMatch == null) {
                    shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
                }
                // 如果通过匹配结果无法立即判断当前方法是否与目标方法匹配,就将匹配得到的
                // ShadowMatch和回调的ShadowMatch封装到DefensiveShadowMatch中
                else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
                    shadowMatch = new DefensiveShadowMatch(shadowMatch,
                            fallbackExpression.matchesMethodExecution(methodToMatch));
                }
                // 缓存结果
                this.shadowMatchCache.put(targetMethod, shadowMatch);
            }
        }
    }
    return shadowMatch;
}

这一部分分析的不是很透彻,有些东西我也没搞太清楚,留在以后单独开一篇分析吧,但是分析至此,已经可以获取到适合给定bean的所有增强,接下来就是创建代理了。




目录
相关文章
|
6天前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
25 6
|
6天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
32 3
|
6天前
|
XML 监控 Java
Spring基础 SpringAOP
Spring基础 SpringAOP
14 0
|
6天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
17 1
|
6天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
108 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
6天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
6天前
|
XML Java uml
spring 源码解析——第一篇(ioc xml方式)
spring 源码解析——第一篇(ioc xml方式)
35 0
|
6天前
|
安全 Java 应用服务中间件
阿里技术官架构使用总结:Spring+MyBatis源码+Tomcat架构解析等
分享Java技术文以及学习经验也有一段时间了,实际上作为程序员,我们都清楚学习的重要性,毕竟时代在发展,互联网之下,稍有一些落后可能就会被淘汰掉,因此我们需要不断去审视自己,通过学习来让自己得到相应的提升。
|
7月前
|
Java 应用服务中间件 Spring
66Spring - 源码解析Spring的启动机制(contextConfigLocation)
66Spring - 源码解析Spring的启动机制(contextConfigLocation)
45 0
|
6天前
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
42 1