2020-07-10

2020-07-10  本文已影响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的依赖。

上一篇下一篇

猜你喜欢

热点阅读