美文网首页
2020-07-10

2020-07-10

作者: 别太扯淡 | 来源:发表于2020-07-10 16:14 被阅读0次

    spring bean重复定义不报错以及注入失败分析

    背景

    @Configuration
    public class GraySwitch {
        @Bean(name = "sellerSupportXconfReader")
        public XconfReader sellerSupportXconfReader(XconfClient xconfClient) {
            XconfReader sellerSupportXconfReader = new XconfReader();
            sellerSupportXconfReader.setXconfClient(xconfClient);
            sellerSupportXconfReader.setGroup(SELLER_SUPPORT_GROUP_NAME);
            return sellerSupportXconfReader;
        }
        
        @Autowired
        private XconfReader sellerSupportXconfReader;
    }
    
    @Configuration
    public class XconfClientConfiguration {
        @Bean(name = "sellerSupportXconfReader")
        public XconfReader sellerSupportXconfReader(XconfClient xconfClient) {
            XconfReader sellerSupportXconfReader = new XconfReader();
            sellerSupportXconfReader.setXconfClient(xconfClient);
            sellerSupportXconfReader.setGroup(SELLER_SUPPORT_GROUP_NAME);
            return sellerSupportXconfReader;
        }
        
        @Bean
        public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(
            @Qualifier("mysqlXconfReader") XconfReader mysqlXconfReader,
            @Qualifier("redisXconfReader") XconfReader redisXconfReader,
            @Qualifier("sellerSupportXconfReader") XconfReader sellerSupportXconfReader) {
            PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();
            placeholderConfigurer.setPropertiesArray(mysqlXconfReader.getProperties(), redisXconfReader.getProperties(),
                sellerSupportXconfReader.getProperties());
            return placeholderConfigurer;
        }
    }
    

    问题1

    现象

    定义了两个name为sellerSupportXconfReader的bean,但没有报错

    分析

    1. 现象1是因为DefaultListableBeanFactory中有这么一行代码:
    private boolean allowBeanDefinitionOverriding = true;
    

    在registerBeanDefinition方法中:

    if (existingDefinition != null) {
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                    "': There is already [" + existingDefinition + "] bound.");
        }
    }
    

    解决

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(SellerSupportApplication.class);
        application.addInitializers(ac -> {
            GenericApplicationContext gac = (GenericApplicationContext) ac;
            gac.setAllowBeanDefinitionOverriding(false);
        });
        application.run(args);
    }
    

    问题2

    现象

    GraySwitch中的sellerSupportXconfReader为null,但启动能成功

    分析

    要搞清楚原因,必须先熟悉spring context加载的过程

    public abstract class AbstractApplicationContext extends DefaultResourceLoader
            implements ConfigurableApplicationContext {
        //other codes
        public void refresh() throws BeansException, IllegalStateException {
            //other codes
            
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
    
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);
    
            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
    
            // Initialize message source for this context.
            initMessageSource();
    
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
    
            // Initialize other special beans in specific context subclasses.
            onRefresh();
    
            // Check for listener beans and register them.
            registerListeners();
    
            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);
    
            // Last step: publish corresponding event.
            finishRefresh();
            //other codes
        }
        //other codes
    }
    

    invokeBeanFactoryPostProcessors中会加载所有的BeanFactoryPostProcessor

    final class PostProcessorRegistrationDelegate {
        //other codes
        public static void invokeBeanFactoryPostProcessors(
                ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
            //other codes
            String[] postProcessorNames =
                    beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    
            // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
            // Ordered, and the rest.
            List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
            List<String> orderedPostProcessorNames = new ArrayList<>();
            List<String> nonOrderedPostProcessorNames = new ArrayList<>();
            for (String ppName : postProcessorNames) {
                if (processedBeans.contains(ppName)) {
                    // skip - already processed in first phase above
                }
                else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
                }
                else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                    orderedPostProcessorNames.add(ppName);
                }
                else {
                    nonOrderedPostProcessorNames.add(ppName);
                }
            }
        }
    }
    

    而Bean的属性填充在AbstractAutowireCapableBeanFactory#populateBean中实现

    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        //other codes
        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
    
        if (hasInstAwareBpps || needsDepCheck) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }
        //other codes
    }
    

    Autowired注解的属性是通过AutowiredAnnotationBeanPostProcessor注入的,依赖检查也是在这个类

    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
    
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            metadata.inject(bean, beanName, pvs);
        }
        catch (BeanCreationException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
        }
        return pvs;
    }
    

    BeanPostProcessor是在registerBeanPostProcessors方法中初始化。

    PropertySourcesPlaceholderConfigurer恰好实现了BeanFactoryPostProcessor,所以会在invokeBeanFactoryPostProcessors这个阶段加载。而PropertySourcesPlaceholderConfigurer依赖sellerSupportXconfReader,后者又依赖GraySwitchGraySwitch又通过Autowired依赖sellerSupportXconfReader。而此时BeanPostProcessor还没初始化,所以不会对GraySwitch赋值,容器也不会初始化失败。

    解决

    把sellerSupportXconfReader定义移到其他类能解决这个问题。或者propertySourcesPlaceholderConfigurer去掉对sellerSupportXconfReader的依赖。

    相关文章

      网友评论

          本文标题:2020-07-10

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