失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > SpringBoot根据配置文件动态创建Bean

SpringBoot根据配置文件动态创建Bean

时间:2023-04-25 19:52:00

相关推荐

SpringBoot根据配置文件动态创建Bean

需求场景:

如果现在需要实现一个这样的需求:

根据配置信息动态控制是否创建任意Bean

通常我们会定义这样的配置:

application.yml: 实现业务需求:根据enbaled控制下面bean-class是否创建

com:tuling:bean:enbaled: true #业务需求:根据enbaled控制下面bean-class是否创建bean-class: com.tuling.beans.TestComponentproperties:id: 1name: xushu

那怎么根据配置动态创建呢:

解:

可以把这个需求拆成获取配置信息动态创建Bean这两步,逐个分析,最后组合即可:

获取配置信息的几种方式:

1、@Value

但是通过@Value单个获取;一个个设置,太麻烦

@Value("${com.tuling.bean.bean-class}")private Class<?> beanClass;// Todo... 一个个获取

2、@ConfigurationProperties

通过@ConfigurationProperties(prefix = “com.tuling”)可以批量获取,比较方便

@ConfigurationProperties("com.tuling.bean")@Component // 如果是自动配置类 请通过@EnableConfigurationProperties启用@Datapublic class BeanProperties {private Boolean enbaled;private Class<?> beanClass;private Map<String,Object> properties;}

3、EnvironmentAware

Spring提供很多XXXAware接口、其中EnvironmentAware接口就可以通过其提供的Environment动态获取。

第一步:实现EnvironmentAware接口

@Componentpublic class TestEnvironmentAware implements EnvironmentAware {@Overridepublic void setEnvironment(Environment environment) {// Todo 绑定配置信息...}}

● 第二步:获取/绑定配置,提供两种方式:

获取方式一:单个获取

public void setEnvironment(Environment environment) {environment.getProperty("com.tuling.bean.bean-class");// ToDo: 一个个获取更多配置信息..}

获取方式二:通过Binder绑定到properties对象

@Overridepublic void setEnvironment(Environment environment) {BindResult<BeanProperties> bindResult = Binder.get(environment).bind("com.tuling.bean", BeanProperties.class);BeanProperties beanProperties= bindResult.get(); }

ok. 3种 获取配置信息的几种方式,你用哪种? 不知道?那接着看吧

动态创建Bean的几种方式:

想想怎么创建bean? 什么?! 用@Bean ???to simple!!!注意!我们需要的是动态!动态!!是在运行过程中经过逻辑代码创建Bean, 不是通过配置、 @Component这种配置方式,这种方式不能自由控制业务逻辑。

想要动态创建Bean先了解Bean创建的大概过程(如果知道,可以跳过章节):

我把Bean的创建过程分为三步:

(出生—> 发育—> 成熟)

相信大家都用喜欢成熟的(徐庶老师也是)想啥呢!正经点:我们在spring应用中也都是用spring帮我们创建的最终的成熟的那个, 但是在这个需求中我们需要在spring发育之前创建,否则无法完成根据配置动态创建,因为你不能直接塞一个成熟的bean给spring容器,那叫早熟!这样bean不健康!!她需要有个过程。

如果想学习更多spring IOC容器的加载过程,可以学习我讲的视频:这里就不过多阐述它的创建过程/video/BV1pL4y1p7Fa?spm_id_from=333.999.0.0

如果想动态注册Bean,我们需要找到“定义态”的扩展接口可以通过先动态注册BeanDefintion即可,Spring提供了动态注册BeanDefinition的接口:

1、ImportBeanDefinitionRegistrar

第一步:创建实现ImportBeanDefinitionRegistrar接口的类, 演示了一个DeanDefintion的注册

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {GenericBeanDefinition beandefinition=new GenericBeanDefinition();beandefinition.setBeanClassName("com.tuling.beans.TestComponent");beandefinition.getPropertyValues().add("id",1);beandefinition.getPropertyValues().add("name","图灵");registry.registerBeanDefinition("testComponent",beandefinition);}}

第二步:结合@Import让它生效

@Import(MyImportBeanDefinitionRegistrar.class)

2、BeanDefinitionRegistryPostProcessor

创建实现BeanDefinitionRegistryPostProcessor接口的类, 演示一个DeanDefintion的注册

public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {GenericBeanDefinition beandefinition=new GenericBeanDefinition();beandefinition.setBeanClassName("com.tuling.beans.TestComponent");beandefinition.getPropertyValues().add("id",1);beandefinition.getPropertyValues().add("name","图灵");registry.registerBeanDefinition("testComponent",beandefinition);}}

3、通过BeanFactoryPostProcessor

BeanFactoryPostProcessor也可以,但是BeanDefinitionRegistryPostProcessor的职责没有BeanFactoryPostProcessor这么明确,BeanFactoryPostProcessor就是是用来注册的,及其他方式就不演示了。

根据配置信息动态创建Bean:

OK. 现在读取信息会了, 动态创建Bean也会了, 结合即可:

怎么结合 ? 想想乛◡乛

可以通过这两种方式:

EnvironmentAware(获取配置)+ImportBeanDefinitionRegistrar(创建Bean)EnvironmentAware(获取配置)+BeanDefinitionRegistryPostProcessor(创建Bean)

通过@Value 和@ConfigurationProperties注解方式获取配置为什么不可以?Why?~

因为顺序原因!这里就要清楚:

@Value 和@ConfigurationProperties注解依赖BeanPostProcessor解析,要调用BeanPostProcessor就要先注册,而BeanPostProcessor的注册是在BeanDefinition的注册之后的。

所以在注册BeanDefinition时是获取不到注解绑定的配置信息的:

实现

EnvironmentAware(获取配置)+BeanDefinitionRegistryPostProcessor(创建Bean)

@Componentpublic class MyBeanDefinitionRegistryPostProcessor implementsBeanDefinitionRegistryPostProcessor,EnvironmentAware {BeanProperties beanProperties;// 根据配置信息进行逻辑控制 动态注册BeanDefintion从而创建Bean@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {GenericBeanDefinition beandefinition=new GenericBeanDefinition();beandefinition.setBeanClass(beanProperties.getBeanClass()); Map<String, Object> properties = beanProperties.getProperties();for (String propertyName : properties.keySet()) {beandefinition.getPropertyValues().add(propertyName,properties.get(propertyName));beandefinition.getPropertyValues().add(propertyName,properties.get(propertyName));}registry.registerBeanDefinition(beandefinition.getBeanClass().getName(),beandefinition);}// 绑定配置信息@Overridepublic void setEnvironment(Environment environment) {BindResult<BeanProperties> bindResult = Binder.get(environment).bind("com.tuling.bean", BeanProperties.class);beanProperties= bindResult.get();}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}}

测试

@Beanpublic CommandLineRunner runner(@Autowired(required = false)TestComponent testComponent){return new CommandLineRunner() {@Overridepublic void run(String... args) throws Exception {System.out.println(testComponent);}};}

● 当com.tuling.bean.enbaledtrue

○ 输出

● 当com.tuling.bean.enbaledfalse

○ 输出

通过这种方式就可以实现根据配置信息自由启用、禁用某些业务的实现 。 当然我这里只是抛砖引玉,实际开发中可以非常灵活:

可以通过配置创建多个bean、通过更复杂的业务逻辑进行控制等等… 学会的同学给个赞吧。

如果觉得《SpringBoot根据配置文件动态创建Bean》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。