spring 源码分析(四)core

Spring 源码分析

sschrodinger

2019/08/14


注解代理如何应用


bean 生命周期

bean 的生命周期如下:

spring 源码分析(四)core_第1张图片
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 advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

protected List findEligibleAdvisors(Class beanClass, String beanName) {
    // step 1.
    List candidateAdvisors = findCandidateAdvisors();
    // step 2.
    List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

public static List findAdvisorsThatCanApply(List 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> 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 searchWithFindSemantics(AnnotatedElement element,
            Set> annotationTypes, @Nullable String annotationName,
            @Nullable Class containerType, Processor processor)

参数解释

  • AnnotatedElement element:当前的 annotatedElement 对象,如 Method
  • Set> 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)