技术笔记:Spring生态研习【五】:Springboot中bean的条件注入

简介: 技术笔记:Spring生态研习【五】:Springboot中bean的条件注入

在springboot中,开发的确变的简单了很多,但是,开发者现在希望开发傻瓜式的方便搞定项目中的各种奇怪的需求最好了,不用烧脑,本来程序猿的生活就是枯燥的,不要再给自己添加更多的烦恼。


今天,就为了方便这点,介绍下,如何解决在开发过程中,一些场景下,为了实现一个配置模块中,基于开关量或者选择配置项,实现不同功能,例如,在一个session共享模块当中,解决session是基于header传递还是基于cookie传递这两种应用场景,有些应用中希望基于header传递sessionId,但是有些应用中希望基于cookie传递sessionId,然后,session共享模块,是一个非常基础的组件,差不多是一个开箱即用的功能块。所以呢,最好能配置好,然后只需要基于配置文件中的某个选项,就能实现运行在不同的工作模式下。这个能否做到呢?真的只需要改一下配置中的开关量就能实现吗?


能否实现,这里卖个关子,先不说,介绍完了本篇博文后,细心的读者一定知道答案,或者说一定能明白能否做,怎么做!


第一大点:先介绍一下springboot中能够支持的或者说封装好的常用的条件注入的注解


1 @ConditionalOnBean


1.1 基本使用案例


@Component


@ConditionalOnBean(name="aBean")


public class BBean {


private final ABean aBean;


public BBean(ABean aBean) {


// ...


}


}


1.2 使用说明


只有当beang的名称为aBean存在的时候,才会注入BBean。


2 @ConditionalOnMissingBean


2.1 基本案例


@Bean


@ConditionalOnMissingBean(name = "notExistsBean")


public BeanToCreate createOneBean() {


return new BeanToCreate("notExistsBean");


}


2.2 使用说明


只有当bean名称为notExistsBean不存在的时候,BeanToCreate类型的bean才会被创建,和@ConditionalOnBean的使用方式相反


3 @ConditionalOnClass


3.1 基本使用案例


@Bean


@ConditionalOnClass(DependedClz.class)


public InjectIfClzExists injectIfClzExists() {


return new InjectIfClzExists("dependedClz");


}


3.2 使用说明


只有当Class为DependedClz.class存在的时候,才会注入类型为InjectIfClzExists的bean,使用上和@ConditionalOnBean有些类似。


4 @ConditionalOnMissingClass


4.1 使用案例


@Bean


@ConditionalOnMissingClass("com.shihuc.bean.clz.DependedClz")


public InjectIfClzNotExists injectIfClzNotExists() {


return new InjectIfClzNotExists("com.shihuc.bean.clz.DependedClz");


}


4.2 使用说明


只有当类com.shihuc.bean.clz.DependedClz不存在的时候,才会注入类型为InjectIfClzNotExists的bean。


5 @ConditionalOnProperty


5.1 基本使用案例


springboot的项目中配置文件application.properties文件中有如下配置:


#.....


section.condition_field=noti


section.condition_property=test


#...


@Bean


@ConditionalOnProperty("section.condition_field")


public PropertyExistBean propertyExistBean() {


return new PropertyExistBean("section.condition_field");


}


5.2 使用说明


主要是根据配置文件中的参数,来决定是否需要创建这个bean,这样就给了我们一个根据配置来控制Bean的选择的手段了,这个非常的好用。因为application.properties文件中存在section.condition_field这个属性,所以,PropertyExistBean这个bean会被创建出来。


5.3 扩展用法


5.3.1 注解定义


@Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.TYPE, ElementType.METHOD })


@Documented


@Conditional(OnPropertyCondition.class)


public @interface ConditionalOnProperty {


/


Alias for {@link #name()}.


@return the names


注意,这个value和name不能同时使用


/


String【】 value() default {};


/


A prefix that should be applied to each property. The prefix automatically ends


with a dot if not specified.


@return the prefix


/


String prefix() default "";


/


The name of the properties to test. If a prefix has been defined, it is applied to


compute the full key of each property. For instance if the prefix is


{@code app.config} and one value is {@code my-value}, the full key would be


{@code app.config.my-value}



Use the dashed notation to specify each property, that is all lower case with a "-"


to separate words (e.g. {@code my-long-property}).


@return the names


*/


String【】 name() default {};


/


The string representation of the expected value for the properties. If not


specified, the property must not be equal to {@code false}.


@return the expected value


/


String havingValue() default "";


/


Specify if the condition should match if the property is not set. Defaults to


{@code false}.


@return if should match if the property is missing


/


boolean matchIfMissing() default false;


}


当我想实现配置文件中存在属性aaa.bbb且其属性的值为ccc时,才注入bean实例DDDD(名为dddd)。


@Bean("dddd")


@ConditionalOnProperty(value="aaa.bbbb", havingValue="ccc")


public DDDD propertyExistBean() {


return new DDDD("aaa.bbb");


}


6 @ConditionalOnExpression


6.1 使用案例


配置文件application.properties中存在下面的配置内容:


conditional.flag=true


java对应代码:


@Bean


@ConditionalOnExpression("#{'true'.equals(environment【'conditional.flag'】)}")


public ExpressTrueBean expressTrueBean() {


return new ExpressTrueBean("express true");


}


6.2 使用说明


相比较前面的Bean,Class是否存在,配置参数property是否存在或者有某个值而言,这个依赖SPEL表达式的,使用起来就功能显得更加强大了;其主要就是执行Spel表达式,根据返回的true/false来判断是否满足条件。


第二大点: spring基于Condition接口和@Conditional注解进行注入bean


这个相当于是条件注入bean的根源解决方案,上述其他几个ConditionalOnXXXX的注解,都是这个Conditional注解的具体场景的定制版,假如没有能够满足自己的应用场景的,或者说要自己实现一个比较特殊的条件注入呢,例如多个条件同时成立之类,怎么办呢,那就需要通过实现Condition接口然后基于@Conditional注解进行使用了。


1 @Conditional注解定义


//此注解可以标注在类和方法上


@Target({ElementType.TYPE, ElementType.METHOD})


@Retention(RetentionPolicy.RUNTIME)


@Documented


public @interface Conditional {


/


All {@link Condition}s that must {@linkplain Condition#matches match}


in order for the component to be registered.


/


Class【】 value();


}


注意,这个注解就一个参数value,且入参是一个Condition的Class的数组。


2 Condition是什么?


@FunctionalInterface


public interface Condition {


/**


Determine if the condition matches.


@param context the condition context


@param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}


or {@link org.springframework.core.type.MethodMetadata method} being checked


@return {@code true} if the condition matches and the component can be registered,


or {@code false} to veto the annotated component's registration


/


boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);


}


3. 使用案例


假设属性配置文件中,有两个环境参数,一个是温度temp,一个是湿度humi,只有当温度高于30度,且湿度大于50个点时,启用Linux,当温度小于30度且湿度小于50个点时,启用Windows,这个只是为了说明在一个@Conditional里面将多个条件满足该如何实现,还有其他的业务场景,可以参照这个案例。


3.1 配置文件参数


#温度数据,摄氏温度


conditional.prop.temp=29


#湿度数据,百分比,这里不带百分号,相当于扩大100倍,使用的时候除以100


conditional.prop.humi=51


3.2 定义bean


有一个HeWoBean的接口,以及两个实现类HelloBean和WorldBean。


/


@Author: chengsh05


@Date: 2019/8/29 16:17


*/


public interface HeWoBean {


public String toString();


}


/


@Author: chengsh05


@Date: 2019/8/29 15:52


/


public class HelloBean implements HeWoBean {


public String getHhh() {


return hhh;


}


public void setHhh(String hhh) {


this.hhh = hhh;


}


public String getEee() {


return eee;


}


public void setEee(String eee) {


this.eee = eee;


}


String hhh;


String eee;


public HelloBean(String hh, String ee) {


this.hhh = hh;


this.eee = ee;


}


@Override


public String toString() {


return this.hhh + ", " + this.eee;


}


}


/**


@Author: chengsh05


@Date: 2019/8/29 15:54


/


public class WorldBean implements HeWoBean {


public String //代码效果参考:http://www.jhylw.com.cn/415925261.html

getWww() {

return www;


}


public void setWww(String www) {


this.www = www;


}


public String getOoo() {


return ooo;


}


public void setOoo(String ooo) {


this.ooo = ooo;


}


String www;


String ooo;


public WorldBean(String ww, String oo) {


this.www = ww;


this.ooo = oo;


}


@Override


public String toString() {


return this.www + ", " + this.ooo;


}


}


3. condition接口实现类及@Conditional应用


/


@Author: chengsh05


@Date: 2019/8/29 9:08


@Description: 配置信息中,温度和湿度条件满足的时候,即当温度temp大于30度,湿度大于50%,启用Linux


/


public class LinuxTime implements Condition {


@Override


public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {


String tempStr = context.getEnvironment().getProperty("conditional.prop.temp");


float temp = Float.valueOf(tempStr);


String humiStr = context.getEnvironment().getProperty("conditional.prop.humi");


float humi = Float.valueOf(humiStr);


if(temp > 30 && humi > 60){


return true;


}


return false;


}


}


/


@Author: chengsh05


@Date: 2019/8/29 9:07


@Description: 配置信息中,温度和湿度条件满足的时候,即当温度temp小于30度,湿度小于50%,启用windows


/


public class WindowsTime implements Condition {


@Override


public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {


String tempStr = context.getEnvironment().getProperty("conditional.prop.temp");


float temp = Float.valueOf(tempStr);


String humiStr = context.getEnvironment().getProperty("conditional.prop.humi");


float humi = Float.valueOf(humiStr);


if(temp < 30 && humi < 60){


return true;


}


return false;


}


}


/


@Author: chengsh05


@Date: 2019/8/29 15:50


*/


@Configuration


public class MyConditional {


@Bean("mybean")


@Conditional(LinuxTime.class)


public HelloBean createHello() {


return new HelloBean("hello", "Linux");


}


@Bean("mybean")


@Conditional(WindowsTime.class)


public WorldBean createWorld() {


return new WorldBean("world", "Windows");


}


}


4.应用验证


/


@Author: chengsh05


@Date: 2019/8/29 16:03


*/


@Controller


@RequestMapping("/condition")


public class ConditionalController {


@Autowired


@Qualifier("mybean")


private HeWoBean myBean;


@RequestMapping("/check")


@ResponseBody


public void check() {


System.out

相关文章
|
20天前
|
XML 安全 Java
|
12天前
|
Java Spring
一键注入 Spring 成员变量,顺序编程
介绍了一款针对Spring框架开发的插件,旨在解决开发中频繁滚动查找成员变量注入位置的问题。通过一键操作(如Ctrl+1),该插件可自动在类顶部添加`@Autowired`注解及其成员变量声明,同时保持光标位置不变,有效提升开发效率和代码编写流畅度。适用于IntelliJ IDEA 2023及以上版本。
一键注入 Spring 成员变量,顺序编程
|
2天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
18 6
|
4天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
32 3
|
24天前
|
监控 Java 数据库连接
详解Spring Batch:在Spring Boot中实现高效批处理
详解Spring Batch:在Spring Boot中实现高效批处理
126 12
|
16天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
24天前
|
安全 Java 测试技术
详解Spring Profiles:在Spring Boot中实现环境配置管理
详解Spring Profiles:在Spring Boot中实现环境配置管理
70 10
|
21天前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
64 5
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
18天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
27 1
下一篇
DataWorks