Java Spring boot在运行时添加和删除单例

Java Spring boot在运行时添加和删除单例,java,spring,spring-boot,applicationcontext,Java,Spring,Spring Boot,Applicationcontext,参考下面的链接,我希望我的SpringBoot应用程序在运行时在applicationcontext中替换bean 下面是我的尝试 MainClass.java @SpringBootApplication public class MainClass { public static void main(String[] args) { SpringApplication.run( MainClass.class, args);

参考下面的链接,我希望我的SpringBoot应用程序在运行时在applicationcontext中替换bean

下面是我的尝试

MainClass.java

@SpringBootApplication
public class MainClass {

public static void main(String[] args) {
        SpringApplication.run(
                MainClass.class, args);

        new Thread(new MyThread()).run();
    }
}
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ConfigurableApplicationContext;

    public class ApplicationContextProvider implements ApplicationContextAware {
        private static ApplicationContext context;

    public static ApplicationContext getApplicationContext(){
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        context = arg0;

    }

    public Object getBean(String name){
        return context.getBean(name, Object.class);
    }

    public void addBean(String beanName, Object beanObject){
        ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext)context).getBeanFactory();
        beanFactory.registerSingleton(beanName, beanObject);
    }

    public void removeBean(String beanName){
        BeanDefinitionRegistry reg = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        reg.removeBeanDefinition(beanName);
    }
}
@Configuration
@ComponentScan(value="com.en.*")
public class Config {

    @Bean
    @Qualifier("myMap")
    public MapBean myMap(){

        MapBean bean = new MapBean();

        Map<String, String> mp = new HashMap<>();
        mp.put("a", "a");
        bean.setMp(mp);
        return bean;
    }

    @Bean
    ApplicationContextProvider applicationContextProvider(){
        return new ApplicationContextProvider();
    }

}
import java.util.Map;

public class MapBean {

    private Map<String, String> mp;

    public Map<String, String> getMp() {
        return mp;
    }

    public void setMp(Map<String, String> mp) {
        this.mp = mp;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("MapBean [mp=");
        builder.append(mp);
        builder.append("]");
        return builder.toString();
    }
}
import java.util.HashMap;
import java.util.Map;

import com.en.model.MapBean;

public class MyThread implements Runnable{

    static ApplicationContextProvider appCtxPrvdr = new ApplicationContextProvider();

    public void run(){
        try {
            Thread.sleep(5000);

            if(ApplicationContextProvider.getApplicationContext().containsBean("myMap")){
                System.out.println("AppCtx has myMap");
                MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
                System.out.println(newM);
                appCtxPrvdr.removeBean("myMap");
                System.out.println("Removed myMap from AppCtx");
            }

            MapBean bean1 = new MapBean();
            Map<String, String> mp = new HashMap<>();
            mp.put("b", "b");
            bean1.setMp(mp);

            appCtxPrvdr.addBean("myMap", bean1);
            System.out.println("myMap added to AppCtx");

            if(ApplicationContextProvider.getApplicationContext().containsBean("myMap")){
                System.out.println("AppCtx has myMap");
                MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
                System.out.println(newM);
                appCtxPrvdr.removeBean("myMap");
                System.out.println("Removed myMap from AppCtx");
            }

            MapBean bean2 = new MapBean();
            Map<String, String> map2 = new HashMap<>();
            map2.put("c", "c");
            bean2.setMp(map2);

            appCtxPrvdr.addBean("myMap", bean2);
            System.out.println("myMap added to AppCtx");
            MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
            System.out.println(newM);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
ApplicationContextProvider.java

@SpringBootApplication
public class MainClass {

public static void main(String[] args) {
        SpringApplication.run(
                MainClass.class, args);

        new Thread(new MyThread()).run();
    }
}
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ConfigurableApplicationContext;

    public class ApplicationContextProvider implements ApplicationContextAware {
        private static ApplicationContext context;

    public static ApplicationContext getApplicationContext(){
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        context = arg0;

    }

    public Object getBean(String name){
        return context.getBean(name, Object.class);
    }

    public void addBean(String beanName, Object beanObject){
        ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext)context).getBeanFactory();
        beanFactory.registerSingleton(beanName, beanObject);
    }

    public void removeBean(String beanName){
        BeanDefinitionRegistry reg = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        reg.removeBeanDefinition(beanName);
    }
}
@Configuration
@ComponentScan(value="com.en.*")
public class Config {

    @Bean
    @Qualifier("myMap")
    public MapBean myMap(){

        MapBean bean = new MapBean();

        Map<String, String> mp = new HashMap<>();
        mp.put("a", "a");
        bean.setMp(mp);
        return bean;
    }

    @Bean
    ApplicationContextProvider applicationContextProvider(){
        return new ApplicationContextProvider();
    }

}
import java.util.Map;

public class MapBean {

    private Map<String, String> mp;

    public Map<String, String> getMp() {
        return mp;
    }

    public void setMp(Map<String, String> mp) {
        this.mp = mp;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("MapBean [mp=");
        builder.append(mp);
        builder.append("]");
        return builder.toString();
    }
}
import java.util.HashMap;
import java.util.Map;

import com.en.model.MapBean;

public class MyThread implements Runnable{

    static ApplicationContextProvider appCtxPrvdr = new ApplicationContextProvider();

    public void run(){
        try {
            Thread.sleep(5000);

            if(ApplicationContextProvider.getApplicationContext().containsBean("myMap")){
                System.out.println("AppCtx has myMap");
                MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
                System.out.println(newM);
                appCtxPrvdr.removeBean("myMap");
                System.out.println("Removed myMap from AppCtx");
            }

            MapBean bean1 = new MapBean();
            Map<String, String> mp = new HashMap<>();
            mp.put("b", "b");
            bean1.setMp(mp);

            appCtxPrvdr.addBean("myMap", bean1);
            System.out.println("myMap added to AppCtx");

            if(ApplicationContextProvider.getApplicationContext().containsBean("myMap")){
                System.out.println("AppCtx has myMap");
                MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
                System.out.println(newM);
                appCtxPrvdr.removeBean("myMap");
                System.out.println("Removed myMap from AppCtx");
            }

            MapBean bean2 = new MapBean();
            Map<String, String> map2 = new HashMap<>();
            map2.put("c", "c");
            bean2.setMp(map2);

            appCtxPrvdr.addBean("myMap", bean2);
            System.out.println("myMap added to AppCtx");
            MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
            System.out.println(newM);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
Config.java

@SpringBootApplication
public class MainClass {

public static void main(String[] args) {
        SpringApplication.run(
                MainClass.class, args);

        new Thread(new MyThread()).run();
    }
}
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ConfigurableApplicationContext;

    public class ApplicationContextProvider implements ApplicationContextAware {
        private static ApplicationContext context;

    public static ApplicationContext getApplicationContext(){
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        context = arg0;

    }

    public Object getBean(String name){
        return context.getBean(name, Object.class);
    }

    public void addBean(String beanName, Object beanObject){
        ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext)context).getBeanFactory();
        beanFactory.registerSingleton(beanName, beanObject);
    }

    public void removeBean(String beanName){
        BeanDefinitionRegistry reg = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        reg.removeBeanDefinition(beanName);
    }
}
@Configuration
@ComponentScan(value="com.en.*")
public class Config {

    @Bean
    @Qualifier("myMap")
    public MapBean myMap(){

        MapBean bean = new MapBean();

        Map<String, String> mp = new HashMap<>();
        mp.put("a", "a");
        bean.setMp(mp);
        return bean;
    }

    @Bean
    ApplicationContextProvider applicationContextProvider(){
        return new ApplicationContextProvider();
    }

}
import java.util.Map;

public class MapBean {

    private Map<String, String> mp;

    public Map<String, String> getMp() {
        return mp;
    }

    public void setMp(Map<String, String> mp) {
        this.mp = mp;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("MapBean [mp=");
        builder.append(mp);
        builder.append("]");
        return builder.toString();
    }
}
import java.util.HashMap;
import java.util.Map;

import com.en.model.MapBean;

public class MyThread implements Runnable{

    static ApplicationContextProvider appCtxPrvdr = new ApplicationContextProvider();

    public void run(){
        try {
            Thread.sleep(5000);

            if(ApplicationContextProvider.getApplicationContext().containsBean("myMap")){
                System.out.println("AppCtx has myMap");
                MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
                System.out.println(newM);
                appCtxPrvdr.removeBean("myMap");
                System.out.println("Removed myMap from AppCtx");
            }

            MapBean bean1 = new MapBean();
            Map<String, String> mp = new HashMap<>();
            mp.put("b", "b");
            bean1.setMp(mp);

            appCtxPrvdr.addBean("myMap", bean1);
            System.out.println("myMap added to AppCtx");

            if(ApplicationContextProvider.getApplicationContext().containsBean("myMap")){
                System.out.println("AppCtx has myMap");
                MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
                System.out.println(newM);
                appCtxPrvdr.removeBean("myMap");
                System.out.println("Removed myMap from AppCtx");
            }

            MapBean bean2 = new MapBean();
            Map<String, String> map2 = new HashMap<>();
            map2.put("c", "c");
            bean2.setMp(map2);

            appCtxPrvdr.addBean("myMap", bean2);
            System.out.println("myMap added to AppCtx");
            MapBean newM = (MapBean) ApplicationContextProvider.getApplicationContext().getBean("myMap", MapBean.class);
            System.out.println(newM);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
因此,根据我下面的理解,事情正在发生

  • 在Config类中,它将myMap添加到appctx
  • 在Mythread类中,它能够在appctx中找到myMap
  • 它可以打印,然后从appctx中删除
  • 它能够将新的myMap添加到appctx
  • 完成上述步骤后。它无法再次删除它

  • 请建议如何多次添加和删除它。

    在春天,定义和bean是完全不同的事情。删除BeanDefinition后,bean仍存在于ApplicationContext中

    因此,我无法真正理解您示例中ApplicationContextProvider的实现

    现在,您要求的是非常不寻常的事情,如果您能够提供更多关于为什么在运行时需要这样一个逻辑的信息,那就太好了

    我个人认为不应该在应用程序启动时删除bean

    有可能或至少是某种“传统”的做法是:

    • 在@Conditional annotation(有很多)/@Profile annotation的帮助下,当应用程序上下文启动时,有条件地加载bean

    • 在运行时更改bean,为其提供额外的功能,用于此用途
      BeanPostProcessor

    • 通过定义BeanFactoryPostProcessor改变Bean定义(在极少数情况下使用)

    现在,如果您知道所有这些机制,但没有一个适合您的需要,请尝试以下方法:

    在单例bean中定义一个内部状态,并在每次调用bean的方法时检查该状态

    这可以通过包装器/装饰器或任何其他方式在bean内部实现,但逻辑是相同的

    例如:

    public class MySingleton {
       private boolean shouldWork = true;
    
       public void stop() {
         shouldWork = false;
       }
    
       public void start() {
         shouldWork = true;
       }
    
    
       public void doSomething() {
             if(shouldWork) {
              // do real logic
             }
             else {
              // do nothing, or some minimal thing to not break anything 
             }
       }
    }
    

    您的逻辑非常复杂,如果您真的想在运行时用不同的配置刷新bean之类的事情, 请考虑看和

    但是,如果您仍然对此不满意,并且需要坚持以上所做的工作,我想问题在于您的方法:

    public void addBean(String beanName, Object beanObject){
        ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext)context).getBeanFactory();
        beanFactory.registerSingleton(beanName, beanObject);
    }
    
    因为它没有注册bean定义,所以spring上下文不会知道它确实存在。 建议尝试添加:

        BeanDefinitionRegistry reg = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        reg.registerBeanDefinition(beanName,beanDefinition);
    
    所以基本上你的addBean方法应该改变如下

    public void addBean(String beanName, Object beanObject){
        ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext)context).getBeanFactory();
        beanFactory.registerSingleton(beanName, beanObject);
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition( beanName );
        BeanDefinitionRegistry reg = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        reg.registerBeanDefinition(beanName,beanDefinition);
    }
    

    既然您链接了Remove和Add bean问题,为什么不使用Add bean one中的一个答案呢?尝试了所有答案,但没有得到最终解决方案。我不知道Conditional&Profile/BeanPostProcessor。我会查一查,然后再打给你。此外,我还需要添加和删除运行时,因为在我的应用程序中,我正在创建套接字对象,并希望在断开连接时重新连接它。重新连接后,我想在APPCTX中存储新的套接字对象。上面的代码只是为了简化我的问题。我使用了BeanPostProcessor。但我不明白如何更改业务逻辑上的bean属性?根据Spring,在bean初始化之前调用postProcessBeforeInitialization,在bean初始化之后调用postProcessAfterInitialization。这些方法调用是自动的。你是在告诉我手动调用postProcessAfterInitialization吗?从第一条注释看,你似乎只需要不创建套接字对象,而是创建一些包含重新连接逻辑的更“复杂”的对象。在这种情况下,您甚至不需要BeanPosProcessor,因为它们在比您的Mark Bramnik更高级的场景中使用:我尝试应用BeanPosProcessor,但它没有反映在应用程序上下文中。我只是在变老。你能帮我一个忙,提供完整的例子吗?我只是觉得你不需要一个bean后处理器。。。见我之前的评论。这还不够好吗?如何在registerBeanDefinition方法调用中创建beanDefinition?编辑了答案,请检查我也尝试过了。但它没有起作用。你能在你的环境中试一试吗?在调用beanFactory.getBeanDefinition(beanName)时,它将抛出异常作为“org.springframework.beans.factory.NoSuchBeanDefinitionException:没有名为“myMap”的bean可用”