当初在配置类外面创立一个bean date

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

工夫并不是变化无穷的,我想要获取以后工夫呢,应该怎么笼罩曾经在容器内bean。我一开始想到应用org..cloud.context.scope.refresh.RefreshScope,然而Spring boot我的项目并没有应用到Spring Cloud包,这个走不通,就试着registerBean动静注册雷同名字bean,想着能不能笼罩容器内bean,毕竟所谓容器只不过是Map而已,只有通过机制笼罩掉Map 上value 就能够实现动静刷新了。

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

执行这个申请,间接报错了,抛出了一个BeanDefinitionOverrideException异样,bean不能被笼罩。在DefaultListableBeanFactory.registerBeanDefinition能够看到其中起因

    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)            throws BeanDefinitionStoreException {                // 省略多余代码        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);        if (existingDefinition != null) { //对于曾经存在bean            if (!isAllowBeanDefinitionOverriding()) { //如果allowBeanDefinitionOverriding 这个值为false 这里就会抛出异样                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);        }     //省略。。

而后发现这个allowBeanDefinitionOverriding 在SpringBoot 刚初始化时,在SpringApplication 会初始化这个值,在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); //设置到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);    }

接着看下配置文件值如何设置到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); //将配置环境bind到属性中        if (!this.isCustomEnvironment) {            environment = convertEnvironment(environment);        }        ConfigurationPropertySources.attach(environment);        return environment;    }    protected void bindToSpringApplication(ConfigurableEnvironment environment) {        try {                        //将配置文件绑定到以后属性上                        //看起来就有ConfigurationProperties 那味了            Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));         }        catch (Exception ex) {//略}    }

在application.properties 增加上面配置

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

重启后从新执行HTTP 申请,没有报错了,从新获取date 这个bean,工夫也变成最新值了。

心得

增加多这一个配置预计为了兼容不同组件间可能存在一些bean 抵触状况,前面初始化bean组件能够笼罩Spring 外部曾经创立组件。如果当初Spring 外部曾经初始化bean A,并且胜利退出到容器中了,这时加载再加载Spring 组件也有一个Class 继承bean A,这是须要增加到容器中。如果没有beanName 雷同笼罩的机制,组件在初始化就会失败。
还有一点值得注意的,registerBean 这个办法只有在容器中删除这个bean 缓存,如何曾经将bean注入到对象属性中,这时这个值不会变动的,须要手动调用beanFactory.getBean("beanName"),因为只有这个bean不存在时候才会执行初始化。如果有这种bean刷新场景能够应用@Lookup来生成一个代理办法。

    @Lookup    public Date initDate() { //这里会将容器内Date类型注入,每次调用办法,从新从容器获取一次        return null;    }