参考以下链接,我希望我的spring boot应用程序在运行时在applicationcontext中替换bean。
下面是我的尝试,
MainClass.java
@SpringBootApplication
public class MainClass {
public static void main(String[] args) {
SpringApplication.run(
MainClass.class, args);
new Thread(new MyThread()).run();
}
}
ApplicationContextProvider.java
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);
}
}
Config.java
@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();
}
}
MapBean.java
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();
}
}
MyThread.java
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();
}
}
}
我得到的输出如下,
AppCtx has myMap
MapBean [mp={a=a}]
Removed myMap from AppCtx
myMap added to AppCtx
AppCtx has myMap
MapBean [mp={b=b}]
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myMap' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.removeBeanDefinition(DefaultListableBeanFactory.java:881)
at com.en.config.ApplicationContextProvider.removeBean(ApplicationContextProvider.java:47)
at com.en.config.MyThread.run(MyThread.java:36)
at java.lang.Thread.run(Unknown Source)
at com.en.MainClass.main(MainClass.java:77)
据我所知,以下情况正在发生。
请咨询如何多次添加和删除它。
答案 0 :(得分:3)
Bean定义和bean在春天完全不同。删除BeanDefinition后,该Bean仍存在于ApplicationContext中。
因此,我不能真正理解您的示例中ApplicationContextProvider的实现。
现在您要的事情很不寻常,如果您可以提供更多有关为什么在运行时需要这样的逻辑的信息,那就太好了。
我个人认为您不应该在应用程序启动时删除bean。
执行以下操作是可能的,或者至少是某种“常规”操作:
当应用程序上下文借助@Conditional批注(其中有很多)/ @Profile批注开始时,有条件地加载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
}
}
}
答案 1 :(得分:0)
您的逻辑很合理,如果您真的想做一些事情,例如在运行时或以某种方式刷新具有不同配置的Bean, 请考虑查看externalized configurations和refresh configs on the fly
但是如果您对此仍然不满意,并且需要坚持上面所做的操作,那么我想问题可能出在您的方法上:
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);
}