Spring — Dynamically register beans in 4 ways At Run-Time

img.png

使用 GenericBeanDefinition 进行动态 Bean 注册

GenericBeanDefinition 是用于标准 bean 定义目的的一站式商店。像任何 bean 定义一样,它允许指定一个类以及可选的构造函数参数值和属性值。此外,可以通过“ parentName ”属性灵活地配置从父 bean 定义派生。
通常,使用这个GenericBeanDefinition类来注册用户可见的 bean 定义(后处理器可能会对其进行操作,甚至可能重新配置父名称)。使用RootBeanDefinition / ChildBeanDefinition,其中父/子关系恰好是预先确定的。

1
2
3
4
5
6
7
8
9
public class MyBean {
private Date date;
public void doSomething () {
System.out.println("from my bean, date: " + date);
}
public void setDate (Date date) {
this.date = date;
}
}

注册上面的,使用GenericBeanDefinition动态创建的 bean 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}
}

Output:

1
from my bean, date: Wed Jult 23 12:20:58 EDT 2019

使用 BeanDefinitionBuilder 动态注册 Bean

使用构建器模式构建BeanDefinitions 的编程方法。

主要用于在实现 Spring 2.0 NamespaceHandlers 时使用。这里唯一的区别是, BeanDefinitionBuilder 使用Builder Pattern。

创建另一个 bean 类。

1
2
3
4
5
6
7
8
9
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 的示例。

1
2
3
4
5
6
7
8
9
10
11
12
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();
}
}

Output:

1
2
from MyBean myStringValue

使用 BeanDefinitionBuilder 注入其他 bean 引用

Creating Bean 1

1
2
3
4
5
6
7
8
9
public class Bean1 {
private Bean2 otherBean;
public void setOtherBean (Bean2 otherBean) {
this.otherBean = otherBean;
}
public void doSomething () {
otherBean.doSomething();
}
}

Creating Bean 2

1
2
3
4
5
public class Bean2 {
public void doSomething () {
System.out.println("from other bean ");
}
}

Setting the Bean2 in Bean1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
}
}

Output:

1
from other bean

Dynamic Bean Registration With BeanFactoryPostProcessor

一个BeanFactoryPostProcessor的可交互和修改bean定义,但从来没有bean实例。允许自定义修改应用程序上下文的 bean 定义,调整上下文底层 bean 工厂的 bean 属性值。应用程序上下文可以在它们的 bean 定义中自动检测BeanFactoryPostProcessor bean,并在创建任何其他 bean 之前应用它们。

  • 创建配置
    1
    2
    3
    4
    5
    6
    7
    @Configuration 
    public class MyConfig {
    @Bean
    MyConfigBean myConfigBean () {
    return new MyConfigBean();
    }
    }

BeanFactoryPostProcessor允许客户端代码自定义 bean 定义。方法BeanFactoryPostProcessor.postProcessBeanFactory在所有 bean 定义加载后由 Spring 启动过程调用,但尚未实例化任何 bean。

1
2
3
4
5
6
7
8
9
10
11
12
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);
}
}

BeanFactoryPostProcessor 示例的主类。

1
2
3
4
5
6
7
8
9
public class MyBean {
private String strProp;
public void setStrProp (String strProp) {
this.strProp = strProp;
}
public void doSomething () {
System.out.println("from MyBean: " + strProp);
}
}

Main class for BeanFactoryPostProcessor Example.

1
2
3
4
5
6
7
8
public class BeanFactoryPostProcessorExample {
public static void main (String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
MyBean bean = context.getBean(MyBean.class);
bean.doSomething();
}
}

Output:

1
2
3
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.

使用 BeanFactoryPostProcessor 动态注册 Bean

对标准BeanFactoryPostProcessor SPI 的扩展,允许在常规 BeanFactoryPostProcessor 检测开始之前注册进一步的 bean 定义。特别是,BeanDefinitionRegistryPostProcessor可以注册进一步的 bean 定义,进而定义BeanFactoryPostProcessor实例。

Creating another config class.

1
2
3
4
5
6
7
@Configuration
public class MyConfig {
@Bean
MyConfigBean myConfigBean () {
return new MyConfigBean();
}
}

这是BeanFactoryPostProcessor的子接口(最后一个例子)。它允许注册 bean 定义。它的方法postProcessBeanDefinitionRegistry在BeanFactoryPostProcessor#postProcessBeanFactory之前被调用。该接口更侧重于 BeanDefinition 注册而不是通用BeanFactoryPostProcessor。
为 BeanDefinitionRegistryPostProcessor 创建实现类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
}
}

Creating a brand new Bean class.

1
2
3
4
5
6
7
8
9
public class MyBean {
private String strProp;
public void setStrProp (String strProp) {
this.strProp = strProp;
}
public void doSomething () {
System.out.println("from MyBean: " + strProp);
}
}

Main class for BeanDefinitionRegistryPostProcessor Example.

1
2
3
4
5
6
7
8
public class BeanDefinitionRegistryPostProcessorExample {
public static void main (String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyConfig.class);
MyBean bean = (MyBean) context.getBean("myBeanName");
bean.doSomething();
}
}

Output:

1
2
3

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'.

Unregistering the Bean at run time

If we need to remove registered beans at runtime, we can do the same as below.
To remove or unregister the bean from spring context.

1
beanRegistry.removeBeanDefinition("bean")

To delete/clear the singleton bean from context.

1
beanRegistry.destroySingleton("bean")

参考文章

评论