11

Spring IoC 公共注解详解

 4 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.
neoserver,ios ssh client

前言

本系列全部基于 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


Recommend

  • 79
    • zhongfucheng.bitcron.com 6 years ago
    • Cache

    Spring IOC知识点一网打尽!

    Spring IOC知识点一网打尽!

  • 80
    • aodeng.cc 6 years ago
    • Cache

    Spring学习笔记(一):IOC-1

    本作品采用 知识共享署名 4.0 国际许可协议 进行许可。 感谢: Java大联盟...

  • 39
    • www.cnblogs.com 6 years ago
    • Cache

    java~spring-ioc的使用

    spring-ioc的使用 IOC容器在很多框架里都在使用,而在spring里它被应用的最大广泛,在框架层面 上,很多功能都使用了ioc技术,下面我们看一下ioc的使用方法。 把服务注册到ioc容器

  • 28
    • www.cnblogs.com 5 years ago
    • Cache

    Spring IOC 的简单使用

    Spring IOC (Inversion Of Control反转控制容器 一、对于IOC容器的简单理解 在java开发中将程序中的对象交给容器管理,而不是在对象的内部管理。 那么两个简单的问题去分析理解IOC容器 1.为什么叫反...

  • 16
    • segmentfault.com 4 years ago
    • Cache

    Spring IoC 到底是什么

    前言 「<span style="color:orangered;font-weight:bold;">上一篇文章</span>[](

  • 31

    Hi ! 我是小小,开始本周的第一篇,本周第一篇内容是关于Spring Boot 最核心的三个注解,将会对这三个注解进行详细解释。 前言 Spring Boot 最大的特点是无需 XML 配置文件,能够实现自动装配,并进行全自动化的jar包...

  • 5

    有部分小伙伴反馈说前面基于注解的Spring中大量使用注解,由于对Java的注解不熟悉,有点难受。建议总结一篇的Java注解的基础知识,那么,它来了! 什么是注解? 如何定义注解 如何使用注解 如何获取注解信息

  • 3
    • xiaomozhang.github.io 2 years ago
    • Cache

    spring5源码 - ioc加载整体流程详解

    通过上篇文章spring5源码 - IOC加载过程 Bean的生命周期知道了spring ioc的加载过程,具体如下图. 下面我们就来对照下图, 看看ioc...

  • 9

    @Component 注解@component代表spring ioc 会把这个类扫描生成Bean实例 @Component public class Role{ @Value("1") private Long id; @Value("role_name_1") private String roleName; @Value("role_no...

  • 13
    • blog.51cto.com 2 years ago
    • Cache

    IOC容器-Bean管理注解方式

    注解开发使用的多、加深了对各个注解的使用理解。 1、什么是注解 (1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…) (2)使用注解,注解作用在类上面,...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK