美文网首页技术干货Java技能图谱
7.spring5源码分析-BeanFactoryPostPro

7.spring5源码分析-BeanFactoryPostPro

作者: 求索 | 来源:发表于2020-03-18 23:37 被阅读0次

    其实你找不到错误不代表错误不存在,同样你看不到技术比你牛的人并不代表世界上没有技术比你牛的人。

    spring 中提供两种bean扩展实现BeanFactoryPostProcessor 和BeanPostProcessor。

    BeanFactoryPostProcessor

    BeanFactory的后置处理器,它的主要功能是参与BeanFactory的建造,通过BeanFactoryPostProcessor可以实现 @ComponentScans扫包、Properties配置加载等功能。下图是spring几个基本后置处理器。

    7.BeanFactoryPostProcessor.png
    • ConfigurationClassPostProcessor:用于解析加了@Configuration的配置类,解析@ComponentScan、@ComponentScans注解扫描的包,以及解析@Import等注解。其入口函数如下:
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
            int registryId = System.identityHashCode(registry);
            if (this.registriesPostProcessed.contains(registryId)) {
                throw new IllegalStateException(
                        "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
            }
            if (this.factoriesPostProcessed.contains(registryId)) {
                throw new IllegalStateException(
                        "postProcessBeanFactory already called on this post-processor against " + registry);
            }
            this.registriesPostProcessed.add(registryId);
            processConfigBeanDefinitions(registry);
        }
    

    该方法做了两件事。一保障processConfigBeanDefinitions一次处理;二、调用方法processConfigBeanDefinitions 。

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
        String[] candidateNames = registry.getBeanDefinitionNames();
        for (String beanName : candidateNames) {
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                    ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
                logger.debug("has already been processed as a configuration class: " );
            } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }
        //省略排序、非空判断、环境准备等
        // Parse each @Configuration class
        ConfigurationClassParser parser = new ConfigurationClassParser(/*省略*/);
           //省略
        do {
            parser.parse(candidates);
            parser.validate();
            Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
            configClasses.removeAll(alreadyParsed);
            // Read the model and create bean definitions based on its content
            if (this.reader == null) {
                this.reader = new ConfigurationClassBeanDefinitionReader( /*省略*/);
            }
            this.reader.loadBeanDefinitions(configClasses);
            alreadyParsed.addAll(configClasses);
             //省略
            for (String candidateName : newCandidateNames) {
                BeanDefinition bd = registry.getBeanDefinition(candidateName);
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                    candidates.add(new BeanDefinitionHolder(bd, candidateName));
                }
            }
            candidateNames = newCandidateNames;
        }
        while (!candidates.isEmpty());
           // ... 
    }
    

    通过源码分析可以看的核心代码分为三部分,以是判断是否为配置类

    ConfigurationClassUtils.checkConfigurationClassCandidate
    //具体实现:
    // full:完成匹配,识别注解 @Configuration
    public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
        return metadata.isAnnotated(Configuration.class.getName());
    }
    
    // 候选匹配 
    // 优先判断注解 @Component、@ComponentScan、@Import、@ImportResource
    // 最后判断 @Bean 注解
    public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
        // 接口中的注解不考虑
        if (metadata.isInterface()) {
            return false;
        } 
        // 任意candidateIndicators注解标记的类
        // candidateIndicators具体值有:Component、ComponentScan、Import、ImportResource
        for (String indicator : candidateIndicators) {
            if (metadata.isAnnotated(indicator)) {
                return true;
            }
        }
        // Finally, let's look for @Bean methods...
        return metadata.hasAnnotatedMethods(Bean.class.getName());
    }
    
    • PropertyResourceConfigurer 使用 ProperResource 自定义 bean 配置信息,核心源码如下:
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Properties mergedProps = mergeProperties();
        // Convert the merged properties, if necessary.
        convertProperties(mergedProps);
        // Let the subclass process the properties.
        processProperties(beanFactory, mergedProps);
    }
    

    PropertyResourceConfigurer 主要做了三件事。

    1. 合并属性
    2. 转换类型
    3. 处理属性
    • CustomAutowireConfigurer 配置自定义自动装配类,CustomAutowireConfigurer是一个BeanFactoryPostProcessor,允许方便地注册自定义autowire限定符类型。即使没有使用Spring的@Qualifier注解

    BeanPostProcessor

    Bean后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下

    public interface BeanPostProcessor {
        /**
         * 在 init-method 或 afterPropertiesSet 等初始化操作之前构造一个新实例返回
           */
        @Nullable
        default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
        /**
         * 在 init-method 或 afterPropertiesSet 等初始化操作之后构造一个新实例返回
           */
        @Nullable
        default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    }
    

    类似修改bean配置信息、bean有效性验证等可以在postProcessBeforeInitialization实现; 一般AOP、代理等都在postProcessAfterInitialization阶段完成。

    7.BeanPostProcessor.png

    上图显示了几个BeanPostProcessor的实现:

    • AbstractAutoProxyCreator 创建代理,在 postProcessBeforeInitialization方法实现代理,源码如下
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
            if (bean != null) {
                Object cacheKey = getCacheKey(bean.getClass(), beanName);
                if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                    return wrapIfNecessary(bean, beanName, cacheKey);
                }
            }
            return bean;
    }
    

    通过 wrapIfNecessary 方法重新封装bean。wrapIfNecessary 方法包括核心代码:

      // Create proxy if we have advice.
            Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = createProxy(
                        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
    

    最后挑战到 createProxy 方法

    protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                @Nullable Object[] specificInterceptors, TargetSource targetSource) {
            // ...
            ProxyFactory proxyFactory = new ProxyFactory();
            proxyFactory.copyFrom(this);
            if (!proxyFactory.isProxyTargetClass()) {
                //todo 设置代理类型
            }
            Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
            proxyFactory.addAdvisors(advisors);
            proxyFactory.setTargetSource(targetSource);
            // ...
            return proxyFactory.getProxy(getProxyClassLoader());
        }
    
    • LoadTimeWeaverAwareProcessor 代码织入处理器

    从织入切面的方式上来看,存在三种织入方式:编译期织入、类加载期织入和运行期织入。编译期织入是指在Java编译期,采用特殊的编译器,将切面织入到Java类中;而类加载期织入则指通过特殊的类加载器,在类字节码加载到JVM时,织入切面;运行期织入则是采用CGLib工具或JDK动态代理进行切面的织入。

    AspectJ采用编译期织入和类加载期织入的方式织入切面,是语言级的AOP实现,提供了完备的AOP支持。它用AspectJ语言定义切面,在编译期或类加载期将切面织入到Java类中。

    AspectJ提供了两种切面织入方式,第一种通过特殊编译器,在编译期,将AspectJ语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;第二种方式是类加载期织入,也简称为LTW(Load Time Weaving)

    核心代码:

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof LoadTimeWeaverAware) {
            LoadTimeWeaver ltw = this.loadTimeWeaver;
            if (ltw == null) {
                Assert.state(this.beanFactory != null,
                        "BeanFactory required if no LoadTimeWeaver explicitly specified");
                ltw = this.beanFactory.getBean(
                        ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
            }
            ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
        }
        return bean;
    }
    

    总结

    • BeanFactoryPostProcessor 工厂后置处理器
    • BeanPostProcessor bean 后置处理器
    • LoadTimeWeaver 类加载期织入
    • AspectJ 切面编程三种方式

    相关文章

      网友评论

        本文标题:7.spring5源码分析-BeanFactoryPostPro

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