Spring — 运行时以四种方式动态注册 bean
1. 概述
在本教程中,我们将学习“使用 spring 动态注册 bean”或“在运行时动态将 bean 添加到 spring-context”。在Spring 应用程序中加载和删除 bean 时,无需在运行时重新启动应用程序即可完成此操作。
如果客户端代码需要注册不受 Spring 容器管理的对象,那么我们将需要使用 BeanDefinition 实例。
Spring 应用程序可以使用 BeanDefinitionRegistry 的以下方法来注册BeanDefinition :
void registerBeanDefinition(String beanName,BeanDefinition beanDefinition)
2. Bean定义
BeanDefinition 描述了一个 bean 实例。它具有 setter 方法,可用于以编程方式将 Spring 特定特征设置为 bean,例如,BeanDefinition #setScope(String scope)可用于设置除默认单例之外的范围。
3. GenericBeanDefinition
这是常用的BeanDefinition实现。
它允许指定 bean 类、bean 特性以及构造函数参数值和属性值。
4.动态注册bean
有很多方法可以实现这一点。我们将通过以下示例进行讨论。
- 通用Bean定义
- BeanDefinitionBuilder
- BeanFactoryPostProcessor
- BeanDefinitionRegistryPostProcessor
4.1 使用GenericBeanDefinition进行动态Bean注册
GenericBeanDefinition 是标准 bean 定义的一站式解决方案。与任何 bean 定义一样,它允许指定类以及可选的构造函数参数值和属性值。此外,可以通过“ parentName ”属性灵活地配置从父 bean 定义派生。
通常,使用此GenericBeanDefinition类来注册用户可见的 bean 定义(后处理器可能会对其进行操作,甚至可能重新配置父名称)。在父/子关系已预先确定的情况下,请使用RootBeanDefinition / ChildBeanDefinition 。
创建示例 Bean 类。
public class MyBean {
private Date date;
public void doSomething () {
System.out.println("来自我的 bean,日期:" + date);
}
public void setDate (Date date) {
this.date = date;
}
}
使用GenericBeanDefinition动态注册上述创建的 bean 。
public class GenericBeanDefinitionExample
{
public static void main (String[] args)
{
DefaultListableBeanFactory context = new DefaultListableBeanFactory();
GenericBeanDefinition gbd = new GenericBeanDefinition();
gbd.setBeanClass(MyBean.class);
MutablePropertyValues mpv = new MutablePropertyValues();
mpv.add("date", new Date());
//alternatively we can use:
// gbd.getPropertyValues().addPropertyValue("date", new Date());
gbd.setPropertyValues(mpv);
context.registerBeanDefinition("myBeanName", gbd);
MyBean bean = context.getBean(MyBean.class);
bean.doSomething();
}
}
输出:
来自我的bean,日期:2019 年 7 月 23 日星期三 12:20:58 EDT
4.2 使用 BeanDefinitionBuilder 进行动态 Bean 注册
使用构建器模式构建BeanDefinitions的编程方法。主要用于实现 Spring 2.0 NamespaceHandlers。这里唯一的区别是,BeanDefinitionBuilder 使用Builder Pattern。
创建另一个 bean 类。
public class MyBean {
private String str;
public void setStr (String str) {
this.str = str;
}
public void doSomething () {
System.out.println("from MyBean " + str);
}
}
使用BeanDefinitionBuilder动态注册 bean 的示例。
public class BeanDefinitionBuilderExample {
public static void main (String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
BeanDefinitionBuilder b = BeanDefinitionBuilder
.rootBeanDefinition(MyBean.class)
.addPropertyValue("str", "myStringValue");
beanFactory.registerBeanDefinition("myBean", b.getBeanDefinition());
MyBean bean = beanFactory.getBean(MyBean.class);
bean.doSomething();
}
}
输出:
from MyBean myStringValue
使用 BeanDefinitionBuilder 注入其他 bean 引用
a. 创建 Bean 1
public class Bean1 {
private Bean2 otherBean;
public void setOtherBean (Bean2 otherBean) {
this.otherBean = otherBean;
}
public void doSomething () {
otherBean.doSomething();
}
}
b. 创建 Bean 2
public class Bean2 {
public void doSomething () {
System.out.println("from other bean ");
}
}
c. 在 Bean1 中设置 Bean2
public class InjectingOtherBeans {
public static void main (String[] args) {
DefaultListableBeanFactory context = new DefaultListableBeanFactory();
//define and register MyOtherBean
GenericBeanDefinition beanOtherDef = new GenericBeanDefinition();
beanOtherDef.setBeanClass(Bean2.class);
context.registerBeanDefinition("other", beanOtherDef);
//definine and register myBean
GenericBeanDefinition beanDef = new GenericBeanDefinition();
beanDef.setBeanClass(Bean1.class);
MutablePropertyValues mpv = new MutablePropertyValues();
mpv.addPropertyValue("otherBean", context.getBean("other"));
beanDef.setPropertyValues(mpv);
context.registerBeanDefinition("myBean", beanDef);
//using MyBean instance
MyBean bean = context.getBean(MyBean.class);
bean.doSomething();
}
}
输出:
from other bean
4.3 使用 BeanFactoryPostProcessor 进行动态 Bean 注册
BeanFactoryPostProcessor可以与 bean 定义交互并修改 bean 实例,但绝不能与 bean 实例交互并修改 bean 定义。允许自定义修改应用程序上下文的 bean 定义,调整上下文底层 bean 工厂的 bean 属性值。应用程序上下文可以自动检测其bean 定义中的BeanFactoryPostProcessor bean,并在创建任何其他 bean 之前应用它们。
创建配置:
@Configuration
public class MyConfig {
@Bean
MyConfigBean myConfigBean () {
return new MyConfigBean();
}
}
BeanFactoryPostProcessor允许客户端代码自定义 bean 定义。BeanFactoryPostProcessor.postProcessBeanFactory 方法由 Spring 启动过程在加载所有 bean 定义但在尚未实例化任何 bean之前调用。
public class MyConfigBean implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)
throws BeansException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(MyBean.class);
bd.getPropertyValues().add("strProp", "my string property");
((DefaultListableBeanFactory) beanFactory).registerBeanDefinition("myBeanName", bd);
}
}
为该 Demo 创建另一个 Bean。
public class MyBean {
private String strProp;
public void setStrProp (String strProp) {
this.strProp = strProp;
}
public void doSomething () {
System.out.println("from MyBean: " + strProp);
}
}
创建BeanFactoryPostProcessor 示例的主类并运行。
public class BeanFactoryPostProcessorExample {
public static void main (String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
MyBean bean = context.getBean(MyBean.class);
bean.doSomething();
}
}
输出:
from MyBean: my string property
WARNING: @Bean method MyConfig.myConfigBean is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.
4.4 使用 BeanDefinitionRegistryPostProcessor 进行动态 Bean 注册
对标准BeanFactoryPostProcessor SPI 的扩展,允许在常规 BeanFactoryPostProcessor 检测启动之前注册进一步的 bean 定义。特别是,BeanDefinitionRegistryPostProcessor可以注册进一步的 bean 定义,而这些 bean 定义又定义BeanFactoryPostProcessor实例。
创建另一个配置类:
@Configuration
public class MyConfig {
@Bean
MyConfigBean myConfigBean () {
return new MyConfigBean();
}
}
这是BeanFactoryPostProcessor (最后一个例子)的子接口。它允许注册 bean 定义。它的方法postProcessBeanDefinitionRegistry在BeanFactoryPostProcessor#postProcessBeanFactory之前被调用。这个接口更专注于 BeanDefinition 注册,而不是通用的BeanFactoryPostProcessor。
为 BeanDefinitionRegistryPostProcessor 创建实现类。
public class MyConfigBean implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry)
throws BeansException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(MyBean.class);
bd.getPropertyValues().add("strProp", "my string property");
registry.registerBeanDefinition("myBeanName", bd);
}
@Override
public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory)
throws BeansException {
//no op
}
}
创建一个全新的 Bean 类。
public class MyBean {
private String strProp;
public void setStrProp (String strProp) {
this.strProp = strProp;
}
public void doSomething () {
System.out.println("from MyBean: " + strProp);
}
}
创建BeanDefinitionRegistryPostProcessor 示例的主类并运行:
public class BeanDefinitionRegistryPostProcessorExample {
public static void main (String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
MyBean bean = (MyBean) context.getBean("myBeanName");
bean.doSomething();
}
}
输出:
from MyBean: my string property
WARNING: Cannot enhance @Configuration bean definition 'beanDefinitionRegistryPostProcessorExample.MyConfig' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
5. 运行时注销 Bean
如果我们需要在运行时删除已注册的bean,我们可以按照下面的方法操作。
从 spring 上下文中删除或者取消注册该 bean。
beanRegistry.removeBeanDefinition("bean")
从上下文中删除/清除单例 bean。
beanRegistry.destroySingleton("bean")
6. 结论
在本文中,我们了解了如何在 Spring 框架中动态注册一个 bean。
本文详细展示了 GenericBeanDefinition、BeanDefinitionBuilder、BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 的示例程序。希望对大家有所帮助,感谢大家拜读~~!