8

Spring IoC 公共注解详解

 3 years ago
source link: http://www.cnblogs.com/leisurexi/p/13269643.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。

什么是公共注解?公共注解就是常见的Java注解,特别是JSR-250中的注解。例如: @Resource@PostConstructor@PreDestroy 等等,本文也就主要分析这三个注解在 Spring 中是如何处理的。

正文

@Resource 注解的处理

@Resource 注解的处理类是 CommonAnnotationBeanPostProcessor ,它通过实现 InstantiationAwareBeanPostProcessor 接口,重写 postProcessProperties() 方法实现对标注了 @Resource 注解的字段或方法的 自动注入

InstantiationAwareBeanPostProcessor 接口的详细信息可以查看 Spring IoC bean 的创建

关于 CommonAnnotationBeanPostProcessor 这个后置处理器是怎么加入到 beanFactory 中的,我们在 Spring IoC component-scan 节点详解 一文中介绍过主要是通过 AnnotationConfigUtils#registerAnnotationConfigProcessors() 实现的。

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
    
    // 省略其他代码...
    // 注册用于处理@Resource、@PostConstructor、@PostDestroy注解的后置处理器
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    
    // 省略其他代码...
    
}

BeanDefinition 合并后的后置处理

CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
    // 寻找需要注入的字段或方法,并封装成 InjectionMetadata
    InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
    // 检查元数据中的注解信息
    metadata.checkConfigMembers(beanDefinition);
}

上面代码中的 findAutowiringMetadata() 方法就是利用反射遍历类的所有字段和方法,找到标注了 @Resource 注解的,并缓存进 injectionMetadataCache 中。

注意:静态字段和静态方法会过滤掉。

findAutowiringMetadata() 方法基本和 AutowiredAnnotationBeanPostProcessor 中的一致,只是处理的注解不同而已,可以查查看 Spring IoC @Autowired 注解详解 一文中该方法的详解。

bean 属性后置处理

CommonAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // 从injectionMetadataCache缓存中获取需要注入的字段和方法
    InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
    try {
        // 进行注入
        metadata.inject(bean, beanName, pvs);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
    }
    return pvs;
}

// InjectMetadata.java
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    // 获取检查后的元素
    Collection<InjectedElement> checkedElements = this.checkedElements;
    // 如果checkedElements不为空就使用checkedElements,否则使用injectedElements
    Collection<InjectedElement> elementsToIterate =
        (checkedElements != null ? checkedElements : this.injectedElements);
    if (!elementsToIterate.isEmpty()) {
        // 遍历elementsToIterate
        for (InjectedElement element : elementsToIterate) {
            if (logger.isTraceEnabled()) {
                logger.trace("Processing injected element of bean '" + beanName + "': " + element);
            }
            // 进行元素注入,见下文详解
            element.inject(target, beanName, pvs);
        }
    }
}

元素注入

InjectionMetadata#inject

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
    throws Throwable {
    // 如果元素是字段
    if (this.isField) {
        // 强转成Field类型
        Field field = (Field) this.member;
        // 并设置为可访问
        ReflectionUtils.makeAccessible(field);
        // 然后使用反射设置值
        field.set(target, getResourceToInject(target, requestingBeanName));
    }
    else {
        // 检查是否跳过
        if (checkPropertySkipping(pvs)) {
            return;
        }
        try {
            // 强转成Method类型
            Method method = (Method) this.member;
            // 并设置为可访问
            ReflectionUtils.makeAccessible(method);
            // 使用反射调用方法
            method.invoke(target, getResourceToInject(target, requestingBeanName));
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

获取注入资源

ResourceElement#getResourceToInject

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
    return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : getResource(this, requestingBeanName));
}

上面的 lazyLookup 就是是否在属性或方法上标注了 @Lazy 注解,该注解先暂不讨论,所以调用后面的 getResource() 方法。

CommonAnnotationBeanPostProcessor#getResource

protected Object getResource(LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException {
	// 省略其它代码...
    return autowireResource(this.resourceFactory, element, requestingBeanName);
}

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException {

    Object resource;
    Set<String> autowiredBeanNames;
    String name = element.name;

    if (factory instanceof AutowireCapableBeanFactory) {
        AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
        DependencyDescriptor descriptor = element.getDependencyDescriptor();
        // 类型匹配(默认为true) && @Resource注解name属性不为空 && 当前beanFactory不包含名称为name的bean
        if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
            autowiredBeanNames = new LinkedHashSet<>();
            // 按类型查找bean
            resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
            if (resource == null) {
                throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
            }
        }
        else {
            // 按名称查找bean
            resource = beanFactory.resolveBeanByName(name, descriptor);
            autowiredBeanNames = Collections.singleton(name);
        }
    }
    // 省略其它代码...

    return resource;
}

上面 autowireResource() 方法中按类型查找的 resolveDependency() 方法在 Spring IoC bean 的创建 一文中分析过,按名称查找 beanresolveBeanByName() 方法实际就是调用 getBean() 通过名称和类型去获取 bean

从上面的代码也可以看出一般情况下 @Resource 注解是按名称注入;而 @Autowired 注解时按类型注入,具体可以查看 Spring IoC @Autowired 注解详解

@PostConstruct、@PreDestroy 注解的处理

处理 @PostConstruct@PreDestroy 注解的处理类是 InitDestroyAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor 继承与该类,相当于注册 CommonAnnotationBeanPostProcessor 时也注册了 InitDestroyAnnotationBeanPostProcessor

BeanDefinition 合并后的后置处理

InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition() 方法是通过 CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition() 方法调用的,如下:

CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 调用InitDestroyAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()方法
    super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
    // 寻找需要注入的字段或方法,并封装成 InjectionMetadata
    InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
    // 检查元数据中的注解信息
    metadata.checkConfigMembers(beanDefinition);
}

InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 寻找需要标注了@PostConstruct和@PreDestroy注解的方法,并封装进LifecycleMetadata
    LifecycleMetadata metadata = findLifecycleMetadata(beanType);
    // 检查元数据中的注解信息
    metadata.checkConfigMembers(beanDefinition);
}

上面代码中的 findLifecycleMetadata() 方法,就是遍历当前初始化的 bean 包括其父类中所有标注了 @PostConstruct@PreDestroy 注解的方法,并封装成 LifecycleMetadata (该类是 InitDestroyAnnotationBeanPostProcessor 中一个内部类),并放入 lifecycleMetadataCache 缓存中。

这里我们简单看一下 LifecycleMetadata 这个类:

private class LifecycleMetadata {

    // 目标类,也就是当前正在初始化的bean
    private final Class<?> targetClass;
	// 存放标注了@PostConstruct的方法
    private final Collection<LifecycleElement> initMethods;
	// 存放标注了@PreDestroy的方法
    private final Collection<LifecycleElement> destroyMethods;

	// 省略其它代码...
}

bean 的初始化前回调

InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 从lifecycleMetadataCache缓存中获取LifecycleMetadata
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    try {
        // 反射调用所有初始化方法
        metadata.invokeInitMethods(bean, beanName);
    }
	// 省略异常处理...
    return bean;
}

看到这里我们知道为什么标注了 @PostConstruct 注解的方法比 InitializingBean#afterPropertiesSet() 方法和自定义初始化方法先调用了;因为其在 bean 的初始化前回调就已经调用了,而剩下的两个是在初始化方法中调用的,详情可以查看 Spring IoC bean 的初始化 一文。

bean 销毁前回调

我们先了解一下 DestructionAwareBeanPostProcessor ,它继承自 BeanPostProcessor ,如下:

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * Bean 销毁前阶段回调
     */
    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

    /**
     * bean实例是否要由此方法销毁
     */
    default boolean requiresDestruction(Object bean) {
        return true;
    }

}

InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction

public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
    // 从lifecycleMetadataCache缓存中获取LifecycleMetadata
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    try {
        // 反射调用所有销毁方法
        metadata.invokeDestroyMethods(bean, beanName);
    }
	// 省略异常处理...
}

和上面的 @PostConstruct 注解一样, @PreDestroy 注解标注的方法也比 DisposableBean#destroy() 方法和自定义销毁方法先调用。

总结

本文主要介绍了 @Resource@PostConstruct@PreDestroy 注解 Spring 是如何对其处理的,可以看出 Spring 的注解驱动大多依靠 实现 BeanPostProcessor 及其子类中的 bean 生命周期各个阶段的回调方法来进行实现的。

最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址: https://github.com/leisurexi/tiny-spring


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK