美文网首页
spring 源码分析(四)core

spring 源码分析(四)core

作者: sschrodinger | 来源:发表于2019-08-14 17:19 被阅读0次

    Spring 源码分析

    sschrodinger

    2019/08/14


    注解代理如何应用


    bean 生命周期

    bean 的生命周期如下:

    image.png

    详见 Spring 源码分析(二)
    ,其中,代理生成最主要的过程为第 9 步,即调用 beanPostProcessor
    postProcessAfterInitialization() 方法。该方法接受一个未被包装的 bean,然后返回一个包装的 bean(即代理的 bean),并将包装的 bean 注入到 spring 上下文容器中

    实际上,Spring 容器上下文初始化时,会将实现了 beanPostProcessor 的类提前初始化,并将其注入到 spring 容器中,接下来初始化其他 bean 时,就会根据注解判断是否 beanPostProcessor 满足当前 bean 的注解需求,如果满足,则需要对 bean 进行处理。

    整个 Spring 容器初始化的过程如下(ApplicationContext 类 的 refresh 方法):

    @Override
       public void refresh() throws BeansException, IllegalStateException {
           synchronized (this.startupShutdownMonitor) {
               //...
    
               // step 1. 获得所有的 bean 定义
               ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
               //...
    
               try {
                   // ...
    
                   // step 2. 将所有的 beanPostProcessor 注入到容器中
                   registerBeanPostProcessors(beanFactory);
    
                   // ... 
                   
                   // step 3. 初始化其他 bean
                   finishBeanFactoryInitialization(beanFactory);
    
                   //...
               }
    
               catch (BeansException ex) {
                   // ...
               }
    
               finally {
                   // ...
               }
           }
       }
    

    我们重点关注上面的三步代码:

    1. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 该方法主要的目的就是获得所有 bean 的定义,封装在一个 beanDefinition 结构中,这个时候并不会初始化任何的 bean
    2. registerBeanPostProcessors(beanFactory); 这个函数就是加载所有的 beanPostProcessor 到容器中。
    3. finishBeanFactoryInitialization(beanFactory); 这一步是具体的初始化。

    方法上的注解

    对于 Aop 来说,可以将注解写在方法上,用于代理的生成,以 @Transactional 注解为例,声明方法如下:

    @Service
    public class ServiceImpl {
        
        @Transactional
        public void findById(long id) {}
        
    }
    

    对于方法上的注解,他的解析过程放在初始化的 step 3 中,我们接下来去分析 step 3。

    我们知道,在初始化一个 bean 时,需要调用 beanPostProcessorpostProcessAfterInitialization() 方法进行包装,在 spring 中,默认使用 InfrastructureAdvisorAutoProxyCreator 这个 beanPostProcessor 处理内置的事务通知,所以我们直接看这个类的方法,如下:

    // 该方法由 InfrastructureAdvisorAutoProxyCreator 的父类 AbstractAutoProxyCreator 提供
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    
    // 该方法由 InfrastructureAdvisorAutoProxyCreator 的父类 AbstractAutoProxyCreator 提供
    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;
        }
    
        // step 1. 获得所有 Intercepter
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // step 2. 利用动态代理生成代理类
            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;
    }
    

    最重点的是 step 1,获得所有的 Intercepter,所谓的 Intercepter 在 JDK 动态代理中,既是实现了 InvocationHandler 的类,只有获得了这个,才能够进行动态代理。

    通过研究 getAdvicesAndAdvisorsForBean,我们可以知道注解在什么时候有效,什么时候无效。

    protected Object[] getAdvicesAndAdvisorsForBean(
            Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }
    
    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        // step 1.
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        // step 2.
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }
    
    public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        // ...
        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;
    }
    
    • step 1 在容器中找到所有的 Advisor 对象,该对象一般持有了一个过滤器,用来过滤不需要代理的方法。
    • step 2 主要是对目标类的方法进行过滤,找到需要过滤的方法

    最终,第二步会调用 AopUtilscanApply 方法,判断当前的目标类是否可以用 Advisor 代理。

    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        // ...
    
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        // ...
    
        Set<Class<?>> classes = new LinkedHashSet<>();
        if (!Proxy.isProxyClass(targetClass)) {
            classes.add(ClassUtils.getUserClass(targetClass));
        }
        classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    
        for (Class<?> clazz : classes) {
            // step 1. 
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
            for (Method method : methods) {
                // step 2.
                if (introductionAwareMethodMatcher != null ?
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }
    
        return false;
    }
    
    • step 1 获得该类及其父类的所有方法。
    • step 2 循环遍历所有的方法,查找是否有满足条件(有注解)的方法。

    @Transactional 注解举例,这个注解所对应的 AdvisorTransactionAttributeSourcePointcut,即会调用 TransactionAttributeSourcePointcutmatches 方法。

    依次进入
    getTransactionAttribute -> computeTransactionAttribute() -> findTransactionAttribute(specificMethod) -> determineTransactionAttribute(method) -> parseTransactionAnnotation(element) -> findMergedAnnotationAttributes() -> searchWithFindSemantics()最终的注解是由 searchWithFindSemantics 函数完成的

    searchWithFindSemantics 的参数形式如下:

    private static <T> T searchWithFindSemantics(AnnotatedElement element,
                Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
                @Nullable Class<? extends Annotation> containerType, Processor<T> processor)
    

    参数解释

    • AnnotatedElement element:当前的 annotatedElement 对象,如 Method
    • Set<Class<? extends Annotation>> annotationTypes:需要查找的 annotation 注解对象集合

    对于事务来说,他所需要查找的注解集合为 Transactionalelement 为对应方法。

    searchWithFindSemantics 寻找策略

    searchWithFindSemantics 函数提供的方法可以防止无止尽的循环寻找注解,使用一个 visit 列表,将访问过的 element 保存在该列表中,保证 element 不会被循环寻找。

    searchWithFindSemantics 在四个地方查找是否有注解:

    • 当前 element 是否有注解(不考虑 @Inherited,即不考虑注解的继承)
    • 如果 element 是方法类型,即 element instanceof Method == true,在定义该 element 的接口中查找注解
    • 如果 element 是方法类型,从该类的直接父类开始,依次在父类中查找该方法的父方法,判断是否有该注解。
    • 如果 element 是类类型,那么会依次查找该类及其父类,看有没有注解。

    note

    • 除了查找直接注解是否所需的注解,该算法还需要递归,注解的注解,即,如果该 element 有注解,但是没有直接注解,那么就需要循环注解,看该注解是否被所需的注解修饰。

    ==综上,使用方法上的注解,不管是在父类中、在接口方法中或者在注解的注解中都可以被找到并加载==

    类上的注解

    同样的,我们也可以把注解写在类或者接口的定义上,如下:

    @Service
    @Transactional
    public class ServiceImpl {
    }
    
    @Transactional
    public interface Service {
    }
    

    我们继续分析如何获得类上的注解定义。

    回到 mputeTransactionAttribute 方法,如下:

    protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    
        // ...
    
        // First try is the method in the target class.
        TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
        if (txAttr != null) {
            return txAttr;
        }
    
        // Second try is the transaction attribute on the target class.
        txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }
    
        // ...
    
        return null;
    }
    

    在方法上获得 txAttr 失败时,会尝试在方法上找到 txAttr,该方法最终也会调用 searchWithFindSemantics 方法,在该类及其父类用依次查找是否有注解。

    ==综上,使用在注解上的注解,不管是在父类中、在接口方法中或者在注解的注解中都可以被找到并加载==。

    ==note==

    • ==不管是注解注解在什么位置,在 spring 中都可以使用代理==
    • ==注意注解的覆盖性:即方法上的注解可以覆盖类上的注解;子类的方法注解可以覆盖父类的方法注解;接口的方法注解可以覆盖父类的方法注解==

    相关文章

      网友评论

          本文标题:spring 源码分析(四)core

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