JavaSpring重新创建特定Bean

JavaSpring重新创建特定Bean,java,spring,singleton,javabeans,Java,Spring,Singleton,Javabeans,我想在运行时重新创建(新对象)一个特定的bean(无需重新启动服务器),以应对一些数据库更改。这就是它的样子- @Component public class TestClass { @Autowired private MyShop myShop; //to be refreshed at runtime bean @PostConstruct //DB listeners public void initializeListener() throws E

我想在运行时重新创建(新对象)一个特定的bean(无需重新启动服务器),以应对一些数据库更改。这就是它的样子-

@Component
public class TestClass {

    @Autowired 
    private MyShop myShop; //to be refreshed at runtime bean

    @PostConstruct //DB listeners
    public void initializeListener() throws Exception {
        //...
        // code to get listeners config
        //...

        myShop.setListenersConfig(listenersConfig);
        myShop.initialize();
    }

    public void restartListeners() {
        myShop.shutdownListeners();
        initializeListener();
    }
}
此代码不会以Spring创建的
myShop
对象作为Singleton运行&除非重新启动服务器,否则不会刷新其上下文。如何刷新(创建新对象)
myShop


我能想到的一个坏方法是在
restartListeners()
中创建新的
myShop
对象,但我觉得这不对。

在DefaultListableBeanFactory中,您有公共方法destroySingleton(“beanName”),所以您可以使用它,但是,您必须知道,如果您的bean自动连接,它将保留最初自动连接的对象的相同实例,您可以尝试以下操作:

@RestController
public class MyRestController  {

        @Autowired
        SampleBean sampleBean;

        @Autowired
        ApplicationContext context;
        @Autowired
        DefaultListableBeanFactory beanFactory;

        @RequestMapping(value = "/ ")
        @ResponseBody
        public String showBean() throws Exception {

            SampleBean contextBean = (SampleBean) context.getBean("sampleBean");

            beanFactory.destroySingleton("sampleBean");

            return "Compare beans    " + sampleBean + "==" 

    + contextBean;

    //while sampleBean stays the same contextBean gets recreated in the context
            }

    }
class Manager implements Runnable {

    private ScheduledExecutorService scheduler;

    public void start() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(this, 0, 120, TimeUnit.SECONDS);
    }

    public void stop() {
        scheduler.shutdownNow();
    }

    @Override
    public void run() {

        try {
            if (KafkaConfiguration.isRefreshNeeded()) {

                AtomicReference<KafkaTemplate<String, String>> kafkaTemplate = 
                    (AtomicReference<KafkaTemplate<String, String>>) Registry.getApplicationContext().getBean("kafkaTemplate");

                // Get new instance here.  This will have the new value for the server list
                // that was "refreshed"
                KafkaConfiguration config = new KafkaConfiguration();

                // The set here replaces the wrapped objet in a thread safe manner with the new bean
                // and thus all injected instances now use the newly created object
                kafkaTemplate.set(config.kafkaTemplate().get());
            }

        } catch (Exception e){

        } finally {

        }
    }
}

它并不漂亮,但显示了你可以如何接近它。如果您处理的是一个控制器而不是一个组件类,那么您可以在方法参数中进行注入,而且它也可以工作,因为Bean只有在方法内部需要时才会被重新创建,至少它看起来是这样的。有趣的问题是,除了它自动连接到的对象之外,还有谁引用了旧Bean,因为它已经从上下文中删除了,我想知道它是否仍然存在,或者如果在上面的控制器中释放它,它是否被垃圾收集,如果上下文中的其他对象引用过它,上述情况会导致问题。

我们有相同的用例。如前所述,在运行时重新创建bean的主要问题之一是如何更新已经注入的引用。这是主要的挑战

为了解决这个问题,我使用了Java的AtomicReference类。我没有直接注入bean,而是将其包装为原子引用,然后注入它。因为AtomicReference包装的对象可以以线程安全的方式重置,所以当检测到数据库更改时,我可以使用它来更改底层对象。下面是此模式的配置/使用示例:

@Configuration
public class KafkaConfiguration {

    private static final String KAFKA_SERVER_LIST = "kafka.server.list";
    private static AtomicReference<String> serverList;

    @Resource
    MyService myService;

    @PostConstruct
    public void init() {
        serverList = new AtomicReference<>(myService.getPropertyValue(KAFKA_SERVER_LIST));
    }

    // Just a helper method to check if the value for the server list has changed
    // Not a big fan of the static usage but needed a way to compare the old / new values
    public static boolean isRefreshNeeded() {

        MyService service = Registry.getApplicationContext().getBean("myService", MyService.class);    
        String newServerList = service.getPropertyValue(KAFKA_SERVER_LIST);

        // Arguably serverList does not need to be Atomic for this usage as this is executed
        // on a single thread
        if (!StringUtils.equals(serverList.get(), newServerList)) {
            serverList.set(newServerList);
            return true;
        }

        return false;
    }

    public ProducerFactory<String, String> kafkaProducerFactory() {

        Map<String, Object> configProps = new HashMap<>();
        configProps.put(ProducerConfig.CLIENT_ID_CONFIG, "...");

        // Here we are pulling the value for the serverList that has been set
        // see the init() and isRefreshNeeded() methods above
        configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, serverList.get());

        configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        return new DefaultKafkaProducerFactory<>(configProps);
    }

    @Bean
    @Lazy
    public AtomicReference<KafkaTemplate<String, String>> kafkaTemplate() {

        KafkaTemplate<String, String> template = new KafkaTemplate<>(kafkaProducerFactory());
        AtomicReference<KafkaTemplate<String, String>> ref = new AtomicReference<>(template);
        return ref;
    }
}
@配置
公共类卡夫卡配置{
私有静态最终字符串KAFKA\u SERVER\u LIST=“KAFKA.SERVER.LIST”;
私有静态原子引用服务器列表;
@资源
我的服务我的服务;
@施工后
公共void init(){
serverList=新的原子引用(myService.getPropertyValue(KAFKA_服务器_列表));
}
//只是一个助手方法,用于检查服务器列表的值是否已更改
//不太喜欢静态用法,但需要一种比较新旧值的方法
公共静态布尔值IsRefreshRequired(){
MyService service=Registry.getApplicationContext().getBean(“MyService”,MyService.class);
字符串newServerList=service.getPropertyValue(KAFKA_服务器_列表);
//可以说,serverList在执行时不需要是原子的
//单线程
如果(!StringUtils.equals(serverList.get(),newServerList)){
set(newServerList);
返回true;
}
返回false;
}
公共生产厂卡夫卡生产厂(){
Map configProps=new HashMap();
configProps.put(ProducerConfig.CLIENT\u ID\u CONFIG,“…”;
//在这里,我们提取已设置的serverList的值
//请参阅上面的init()和isRefreshRequired()方法
configProps.put(ProducerConfig.BOOTSTRAP\u SERVERS\u CONFIG,serverList.get());
configProps.put(ProducerConfig.KEY\u SERIALIZER\u CLASS\u CONFIG,StringSerializer.CLASS);
configProps.put(ProducerConfig.VALUE\u SERIALIZER\u CLASS\u CONFIG,StringSerializer.CLASS);
返回新的DefaultKafkaProducerFactory(configProps);
}
@豆子
@懒惰的
公共原子引用kafkaTemplate(){
KafkatTemplate=新的KafkatTemplate(kafkaProducerFactory());
AtomicReference ref=新的AtomicReference(模板);
返回ref;
}
}
然后在需要的地方注入bean,例如

public MyClass1 {

    @Resource 
    AtomicReference<KafkaTemplate<String, String>> kafkaTemplate;
    ...
}

public MyClass2 {

    @Resource 
    AtomicReference<KafkaTemplate<String, String>> kafkaTemplate;
    ...
}
公共MyClass1{
@资源
原子参考卡夫卡模板;
...
}
公共MyClass2{
@资源
原子参考卡夫卡模板;
...
}
在一个单独的类中,我运行一个调度程序线程,该线程在应用程序上下文启动时启动。这个类看起来像这样:

@RestController
public class MyRestController  {

        @Autowired
        SampleBean sampleBean;

        @Autowired
        ApplicationContext context;
        @Autowired
        DefaultListableBeanFactory beanFactory;

        @RequestMapping(value = "/ ")
        @ResponseBody
        public String showBean() throws Exception {

            SampleBean contextBean = (SampleBean) context.getBean("sampleBean");

            beanFactory.destroySingleton("sampleBean");

            return "Compare beans    " + sampleBean + "==" 

    + contextBean;

    //while sampleBean stays the same contextBean gets recreated in the context
            }

    }
class Manager implements Runnable {

    private ScheduledExecutorService scheduler;

    public void start() {
        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(this, 0, 120, TimeUnit.SECONDS);
    }

    public void stop() {
        scheduler.shutdownNow();
    }

    @Override
    public void run() {

        try {
            if (KafkaConfiguration.isRefreshNeeded()) {

                AtomicReference<KafkaTemplate<String, String>> kafkaTemplate = 
                    (AtomicReference<KafkaTemplate<String, String>>) Registry.getApplicationContext().getBean("kafkaTemplate");

                // Get new instance here.  This will have the new value for the server list
                // that was "refreshed"
                KafkaConfiguration config = new KafkaConfiguration();

                // The set here replaces the wrapped objet in a thread safe manner with the new bean
                // and thus all injected instances now use the newly created object
                kafkaTemplate.set(config.kafkaTemplate().get());
            }

        } catch (Exception e){

        } finally {

        }
    }
}
类管理器实现可运行{
专用ScheduledExecutorService调度器;
公开作废开始(){
scheduler=Executors.newSingleThreadScheduledExecutor();
scheduleAtFixedRate(这个,0,120,TimeUnit.SECONDS);
}
公共停车场(){
scheduler.shutdownNow();
}
@凌驾
公开募捐{
试一试{
if(kafkanconfiguration.isrefreshRequired()){
原子引用kafkaTemplate=
(AtomicReference)Registry.getApplicationContext().getBean(“kafkaTemplate”);
//在此处获取新实例。这将具有服务器列表的新值
//这是“刷新”
卡夫卡配置=新卡夫卡配置();
//这里的集合用新bean以线程安全的方式替换包装好的objet
//因此,所有注入的实例现在都使用新创建的对象
kafkaTemplate.set(config.kafkaTemplate().get());
}
}捕获(例外e){
}最后{
}
}
}
我仍然在犹豫,如果这是我会提倡做的事情,因为它确实有轻微的气味。但在有限和谨慎的使用中,它确实提供了一种替代方法来描述用例。请注意,从卡夫卡的角度来看,这个代码示例将保留旧的生产者。实际上,需要正确地对旧生产者执行flush()调用才能关闭它。但这并不是本例的目的。

beans