在我们开发的过程中,使用静态配置信息,即可由Spring容器生成我们需要的Bean。但是在一些场景下,我们需要Spring容器根据我们的配置信息动态的生成Bean,这时就需要将Bean动态的注册到Spring容器中去。
一般而言,我们使用@Componment、@Service等注解,就可以向Spring容器注册Bean,下面介绍几种方式动态注入Bean到Spring容器。
具体的应用场景,可以参考我写的一个简单快捷的rocketmq-spring-boot-starter里的实现。
public class NormalBean1 {
public NormalBean1() {
System.out.println("NormalBean1()");
}
public void print() {
System.out.println("in NormalBean1.print()");
}
}
public class NormalBean2 {
@Autowired
private NormalBean1 normalBean1;
public NormalBean2() {
System.out.println("NormalBean2()");
}
public void print() {
System.out.println("in NormalBean2.print()");
System.out.println("调用NormalBean1.print()");
normalBean1.print();
}
}
public class NormalBean3 {
@Autowired
private ServiceBean1 serviceBean1;
public NormalBean3() {
System.out.println("NormalBean3()");
}
public void print() {
System.out.println("in NormalBean3.print()");
System.out.println("调用ServiceBean1.print()");
serviceBean1.print();
}
}
@Service
public class ServiceBean1 {
public ServiceBean1() {
System.out.println("ServiceBean1()");
}
public void print() {
System.out.println("in ServiceBean1.print()");
}
}
@Service
public class ServiceBean2 {
@Autowired
private NormalBean1 normalBean1;
public ServiceBean2() {
System.out.println("ServiceBean2()");
}
public void print() {
System.out.println("in ServiceBean2.print()");
System.out.println("调用NormalBean1.print()");
normalBean1.print();
}
}
动态注册Bean,是通过BeanDefinitionBuilder、BeanDefinition、BeanDefinitionRegistry来将Bean动态的注册到容器中去。
@Configuration
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
registerBean("normalBean1", NormalBean1.class, beanDefinitionRegistry);
registerBean("normalBean2", NormalBean2.class, beanDefinitionRegistry);
registerBean("normalBean3", NormalBean3.class, beanDefinitionRegistry);
}
private void registerBean(String name, Class<?> clz, BeanDefinitionRegistry beanDefinitionRegistry) {
if (beanDefinitionRegistry.isBeanNameInUse(name)) {
throw new RuntimeException("exist beanName");
}
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clz);
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition(name, beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
//这个方法和上面的方法作用其实是一样的,一般使用上面的方式进行动态注入
}
}
@Configuration
public class BeanDefinitionConfiguration {
public BeanDefinitionConfiguration(ApplicationContext applicationContext) {
System.out.println("ManualConfiguration init");
registerBean((ConfigurableApplicationContext) applicationContext);
}
private void registerBean(ConfigurableApplicationContext applicationContext) {
registerBean(applicationContext, "normalBean1", NormalBean1.class);
registerBean(applicationContext, "normalBean2", NormalBean2.class);
registerBean(applicationContext, "normalBean3", NormalBean3.class);
}
public <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clz, Object... args) {
if (applicationContext.containsBean(name)) {
Object bean = applicationContext.getBean(name);
if (bean.getClass().isAssignableFrom(clz)) {
return (T) bean;
} else {
throw new RuntimeException("BeanName重复 " + name);
}
}
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clz);
for (Object arg : args) {
beanDefinitionBuilder.addConstructorArgValue(arg);
}
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
beanDefinitionRegistry.registerBeanDefinition(name, beanDefinition);
return applicationContext.getBean(name, clz);
}
}
@Configuration
public class BeanDefinitionConfiguration2 implements ApplicationContextAware, InitializingBean {
private ConfigurableApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
@Override
public void afterPropertiesSet() {
registerBean(applicationContext);
}
private void registerBean(ConfigurableApplicationContext applicationContext) {
registerBean(applicationContext, "normalBean1", NormalBean1.class);
registerBean(applicationContext, "normalBean2", NormalBean2.class);
registerBean(applicationContext, "normalBean3", NormalBean3.class);
}
public <T> T registerBean(ConfigurableApplicationContext applicationContext, String name, Class<T> clz, Object... args) {
if (applicationContext.containsBean(name)) {
Object bean = applicationContext.getBean(name);
if (bean.getClass().isAssignableFrom(clz)) {
return (T) bean;
} else {
throw new RuntimeException("BeanName重复 " + name);
}
}
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clz);
for (Object arg : args) {
beanDefinitionBuilder.addConstructorArgValue(arg);
}
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
beanDefinitionRegistry.registerBeanDefinition(name, beanDefinition);
return applicationContext.getBean(name, clz);
}
}
##### 3、普通方式3
```java
@Configuration
public class SingletonBeanRegister implements BeanFactoryAware, InitializingBean {
private ConfigurableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
beanFactory.registerSingleton("myBean3", new MyBean("MyBean3"));
beanFactory.registerSingleton("myBean4", new MyBean("MyBean4"));
}
}
普通方式1和普通方式2还可以加载properties,即通过@Autowired引入定义的Properties属性,但是继承BeanDefinitionRegistryPostProcessor的方式不可以,因为postProcessBeanDefinitionRegistry方法会更早执行,更具体的原因目前还没有一个更深入的探究,先记一下。
BeanDefinitionBuilder还有设置属性的方法,一般使用以下两种
//这一种通过构造函数依次将属性set进去
beanDefinitionBuilder.addConstructorArgValue("fieldValue");
//这一种是set某个具体的属性
beanDefinitionBuilder.addPropertyValue("fieldName", "fieldValue");
@SpringBootTest
@RunWith(SpringRunner.class)
public class BeanDefinitionApplicationTests {
@Autowired
NormalBean1 normalBean1;
@Autowired
NormalBean2 normalBean2;
@Autowired
NormalBean3 normalBean3;
@Autowired
ServiceBean1 serviceBean1;
@Autowired
ServiceBean2 serviceBean2;
@Test
public void test() {
normalBean1.print();
normalBean2.print();
normalBean3.print();
serviceBean1.print();
serviceBean2.print();
}
}
测试结果:
Bean实例化
ServiceClass1()
ServiceClass2()
NormalClass1()
NormalClass2()
NormalClass3()
//这些是控制台打印的实例化Bean的语句(二中的Bean里,构造函数会打印这些信息)
调用结果
in NormalBean1.print()
in NormalBean2.print()
调用NormalBean1.print()
in NormalBean1.print()
in NormalBean3.print()
调用ServiceBean1.print()
in ServiceBean1.print()
in ServiceBean1.print()
in ServiceBean2.print()
调用NormalBean1.print()
in NormalBean1.print()
在实际使用过程中,Spring启动时扫描自定义注解,是通过BeanFactoryPostProcessor接口的postProcessBeanFactory方法,configurableListableBeanFactory.getBeansWithAnnotation(AutoDiscoverClass.class);获取每一个有自定义注解的Bean。这种方式,不适合当存在一些特殊的需求。而我们通过动态注册Bean可以满足我们特定的需求。