• Spring IoC 公共注解详解


    前言

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

  • 相关阅读:
    UpdatePanel 脚本失效的解决
    DataTable 中的查询、排序及分页(c#)
    如何让Gridview在没有数据的时候显示表头
    Jquery 对.net服务器控件RadioButtonList进行赋值和取值的操作
    给Repeater控件里添加序号的5种方法
    C#缓存absoluteExpiration、slidingExpiration两个参数的疑惑
    筛选DataTable数据的方法
    CSS前端基础应用实践
    js实现页面自动刷新
    什么是MVC
  • 原文地址:https://www.cnblogs.com/leisurexi/p/13269643.html
Copyright © 2020-2023  润新知