Spring覆蓋已初始化bean

現(xiàn)在在配置類里面創(chuàng)建一個(gè)bean date

@Configuration
public class DateConfig {

    @Bean("date")
    public Date  date(){
        return new Date();
    }
}

時(shí)間并不是一成不變的,我想要獲取當(dāng)前時(shí)間呢,應(yīng)該怎么覆蓋已經(jīng)在容器內(nèi)bean。我一開始想到使用org..cloud.context.scope.refresh.RefreshScope,但是Spring boot項(xiàng)目并沒有使用到Spring Cloud包,這個(gè)走不通,就試著registerBean動(dòng)態(tài)注冊相同名字bean,想著能不能覆蓋容器內(nèi)bean,畢竟所謂容器只不過是Map而已,只要通過機(jī)制覆蓋掉Map 上value 就可以實(shí)現(xiàn)動(dòng)態(tài)刷新了。

    private ApplicationContext applicationContext;

    @GetMapping("setting/now")
    public void dkd(){
        GenericApplicationContext gac = (GenericApplicationContext)applicationContext;
        gac.registerBean("date",Date.class);
    }

執(zhí)行這個(gè)請(qǐng)求,直接報(bào)錯(cuò)了,拋出了一個(gè)BeanDefinitionOverrideException異常,bean不能被覆蓋。在DefaultListableBeanFactory.registerBeanDefinition可以看到其中原因

    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
                // 省略多余代碼
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) { //對(duì)于已經(jīng)存在bean
            if (!isAllowBeanDefinitionOverriding()) { //如果allowBeanDefinitionOverriding 這個(gè)值為false 這里就會(huì)拋出異常
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) { //這里是BeanDefinition  
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (logger.isInfoEnabled()) {
                    logger.info("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
     //省略。。

然后發(fā)現(xiàn)這個(gè)allowBeanDefinitionOverriding 在SpringBoot 剛初始化時(shí),在SpringApplication 會(huì)初始化這個(gè)值,在SpringApplication.prepareContext

    private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
        listeners.contextPrepared(context);
        bootstrapContext.close(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
            ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
            if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); //設(shè)置到DefaultListableBeanFactory中
            }
        }
        if (this.lazyInitialization) { //開啟懶加載配置
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

接著看下配置文件值如何設(shè)置到SpringApplication.allowBeanDefinitionOverriding

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
        // Create and configure the environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        listeners.environmentPrepared(bootstrapContext, environment);
        DefaultPropertiesPropertySource.moveToEnd(environment);
        Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
                "Environment prefix cannot be set via properties.");
        bindToSpringApplication(environment); //將配置環(huán)境bind到屬性中
        if (!this.isCustomEnvironment) {
            environment = convertEnvironment(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

    protected void bindToSpringApplication(ConfigurableEnvironment environment) {
        try {
                        //將配置文件綁定到當(dāng)前屬性上
                        //看起來就有ConfigurationProperties 那味了
            Binder.get(environment).bind("spring.main", Bindable.ofInstance(this)); 
        }
        catch (Exception ex) {//略}
    }

在application.properties 添加下面配置

spring.main.allow-bean-definition-overriding=true

重啟后重新執(zhí)行HTTP 請(qǐng)求,沒有報(bào)錯(cuò)了,重新獲取date 這個(gè)bean,時(shí)間也變成最新值了。

心得

添加多這一個(gè)配置估計(jì)為了兼容不同組件間可能存在一些bean 沖突情況,后面初始化bean組件可以覆蓋Spring 內(nèi)部已經(jīng)創(chuàng)建組件。假如現(xiàn)在Spring 內(nèi)部已經(jīng)初始化bean A,并且成功加入到容器中了,這時(shí)加載再加載Spring 組件也有一個(gè)Class 繼承bean A,這是需要添加到容器中。如果沒有beanName 相同覆蓋的機(jī)制,組件在初始化就會(huì)失敗。
還有一點(diǎn)值得注意的,registerBean 這個(gè)方法只有在容器中刪除這個(gè)bean 緩存,如何已經(jīng)將bean注入到對(duì)象屬性中,這時(shí)這個(gè)值不會(huì)變化的,需要手動(dòng)調(diào)用beanFactory.getBean("beanName"),因?yàn)橹挥羞@個(gè)bean不存在時(shí)候才會(huì)執(zhí)行初始化。如果有這種bean刷新場景可以使用@Lookup來生成一個(gè)代理方法。

    @Lookup
    public Date initDate() { //這里會(huì)將容器內(nèi)Date類型注入,每次調(diào)用方法,重新從容器獲取一次
        return null;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容