springboot:@ConditionalOnProperty根据不同时机注入不同实现的bean
一、引言
在开发中经常会碰到这样的情形,一个接口会有不同的实现,但在开发中都是基于接口的注入,那么怎么根据不同的需求注入不同的类型就是一个值得考虑的问题。在注入属性时常用的两个注解是@Autowired和@Resource,使用它们便可以实现,同时spring提供了很多@ConditionalXXX的注解,可以很好的完成上述功能;
二、代码演示
1、问题代码描述
使用代码的方式描述下上面提到的问题,后面给出解决方案。
controller类,TestConditionalOnProperty.java
package com.atssg.controller; import com.atssg.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
public class TestConditionalOnProperty {
//注入MyService
@Autowired
private MyService myService; @GetMapping("/test/test1")
public void test(){ myService.test();
} }
下面是MyService接口,MyService.java
package com.atssg.service;
public interface MyService {
void test();
}
下面是两个实现类,MyServiceImpl.java
package com.atssg.service.impl; import com.atssg.service.MyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; @Service
@Slf4j
public class MyServiceImpl implements MyService { @Override
public void test() {
log.info("I am Myservice");
}
}
下面是MyServiceImpl2.java
package com.atssg.service.impl; import com.atssg.service.MyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; @Service
@Slf4j
public class MyServiceImpl2 implements MyService { @Override
public void test() {
log.info("I am MyServiceImpl2");
}
}
程序启动报错,
Description: Field myService in com.atssg.controller.TestConditionalOnProperty required a single bean, but 2 were found:
- myServiceImpl: defined in file [D:\code\cloud2020\cloud-sync-7002\target\classes\com\atssg\service\impl\MyServiceImpl.class]
- myServiceImpl2: defined in file [D:\code\cloud2020\cloud-sync-7002\target\classes\com\atssg\service\impl\MyServiceImpl2.class]
大体意思是TestConditionalOnProperty需要一个单例bean,但是发现了两个,也就是MyServiceImpl和MyServicImpl2。那如何才能注入一个那。
2、解决方案
2.1、@Qualifier
@Autowired默认条件下会按照id注入,找不到id会按照类型注入,上面的错误便是这种情况,我们可以给@Autowired指定要注入的id即可,使用@Qualifier可以实现指定id。
TestConditionalOnProperty改动如下,
package com.atssg.controller; import com.atssg.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
public class TestConditionalOnProperty { @Qualifier("myServiceImpl2")
@Autowired
private MyService myServiceImpl; @GetMapping("/test/test1")
public void test(){ myServiceImpl.test();
} }
同理,两个MyService的实现类也要指定生成bean的id。默认情况下是其类名首字母小写,如MyServiceImpl如果不指定生成id则为myServiceImpl。修改如下
MyServiceImpl.java
package com.atssg.service.impl; import com.atssg.service.MyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; @Service("myServiceImpl")
@Slf4j
public class MyServiceImpl implements MyService { @Override
public void test() {
log.info("I am Myservice");
}
}
MyServiceImpl2.java
package com.atssg.service.impl; import com.atssg.service.MyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; @Service("myServiceImpl2")
@Slf4j
public class MyServiceImpl2 implements MyService { @Override
public void test() {
log.info("I am MyServiceImpl2");
}
}
@Service可以指定value值,也就是指定生成bean的id值。
测试结果如下,

从上面可以看出已经可以实现注入一个MyService的实现类,但是这种方式有一个弊端,那就是不够灵活,虽然实现了加载一个实现类,但是每次都需要修改代码,而且有可能会修改错误,而且是硬编码。
其实还有另外一种方式,也是使用@Autowired,只不过被@Autowired注解修饰的变量名必须是要注入的bean的id,如

这里注入的是myServiceImpl,也就是MyServiceImpl的实现类,测试结果如下,

这样便实现了注入一个bean的目的,但这种方式和上面的方式是一样的,不够灵活且是硬编码。下面看springboot为我们提供的另外一种方式。
2.2、@ConditionalOnProperty
@ConditionalOnProperty注解是springboot开发的众多@ConditionalXX注解中的一个,根据properties文件中的属性值来决定注入哪一个。
先在applicaiton.properties文件中定义一个变量
myService=service1
TestConditionalOnProperty中使用@Resource进行注入
package com.atssg.controller; import com.atssg.service.MyService; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController
public class TestConditionalOnProperty { @Resource
private MyService myService; @GetMapping("/test/test1")
public void test(){ myService.test();
} }
MyService的两个实现类,MyServiceImpl.java
package com.atssg.service.impl; import com.atssg.service.MyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component; @Component
@ConditionalOnProperty(name = "myService",havingValue = "service1")
@Slf4j
public class MyServiceImpl implements MyService { @Override
public void test() {
log.info("I am Myservice");
}
}
MyServiceImpl2.java
package com.atssg.service.impl; import com.atssg.service.MyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component; @Component
@ConditionalOnProperty(name = "myService",havingValue = "service2")
@Slf4j
public class MyServiceImpl2 implements MyService { @Override
public void test() {
log.info("I am MyServiceImpl2");
}
}
每个实现类中均使用了@ConditionalOnProperty注解,并指定了name和havingValue属性,name指定applicaiton.properties文件中的属性名,havingValue指定了属性值,在上面的配置的是service1,即调用MyServiceImpl中的test()方法,测试如下
2021-09-20 19:18:44.499 INFO 23892 --- [)-192.168.117.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
2021-09-20 19:23:38.576 INFO 23892 --- [nio-8080-exec-1] com.atssg.service.impl.MyServiceImpl : I am Myservice
从测试结果来看,调用的的确是MyServiceImpl中的test()方法,那么改成service2的结果如下,

这样只需要修改配置文件便可以实现动态加载不同的实现类。
三、总结
要想实现加载不同的实现类,还有其他的方式,这里不一一列举,本文旨在介绍@ConditionalOnProperty注解的使用。@ConditionalOnProperty注解可以实现根据配置文件中的值注入不同的实现类。

springboot:@ConditionalOnProperty根据不同时机注入不同实现的bean的更多相关文章
- SpringBoot 为什么能够自动的注入一些常用的Bean ?
原文转载至:https://blog.csdn.net/qq_29941401/article/details/79605388 但是我一直没有搞懂druid是怎么自动配置的? 这个是properti ...
- SpringBoot 为什么能够自动的注入一些常用的Bean ?详细分析SpringBoot 自动配置的实现
转载至:https://blog.csdn.net/qq_29941401/article/details/79605388 有一个问题一直让我好奇,为什么在SpringBoot中有的bean 我们都 ...
- SpringBoot拦截器中无法注入bean的解决方法
SpringBoot拦截器中无法注入bean的解决方法 在使用springboot的拦截器时,有时候希望在拦截器中注入bean方便使用 但是如果直接注入会发现无法注入而报空指针异常 解决方法: 在注册 ...
- Springboot @ConditionalOnProperty注解
最近看了一段代码其中用到了@ConditionalOnProperty注解,直接没有了解过这个注解,今天看到了顺便了解一下 具体代码如下 @Configuration public class Web ...
- SpringBoot之Spring@Value属性注入使用详解
在使用Spring框架的项目中,@Value是使用比较频繁的注解之一,它的作用是将配置文件中key对应的值赋值给它标注的属性.在日常使用中我们常用的功能都比较简单,本篇文章系统的带大家来了解一下@Va ...
- springboot的4种属性注入
1.Autowired注入 2.构造方法注入 3.@Bean方法形参注入 4.直接在@Bean方法上使用注解@ConfigurationProperties(prefix="jdbc&quo ...
- SpringBoot 之 普通类获取Spring容器中的bean
[十]SpringBoot 之 普通类获取Spring容器中的bean 我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器 ...
- Solon 开发,二、注入或手动获取Bean
Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...
- 如何注入值到Spring bean属性
在Spring中,有三种方式注入值到 bean 属性. 正常的方式 快捷方式 “p” 模式 看到一个简单的Java类,它包含两个属性 - name 和 type.稍后将使用Spring注入值到这个 b ...
随机推荐
- excel的导入与导出---通用版
excel的导入与导出---通用版 web项目关于导入导出的业务场景很常见,最近我就又遇到了这个业务场景.这次将最近半个月做的导入导出总结一下 使用的pom如下,主要还是阿里巴巴的easyexcel依 ...
- Java 多线程与并发【原理第二部分笔记】
Java 多线程与并发[原理第二部分笔记] 什么是Java内存模型中的happens-before Java内存模型,即JMM,本身是一种抽象的概念,并不是真实存在的,他描述的是一组规则或者说是一种规 ...
- 【大咖直播】Elastic Security 安全管理实战工作坊
本次实战课程,旨在用 Elastic Security 来武装每一位安全运维人员,从容预防.检测和应对网络威胁.这款免费开放的解决方案提供了 SIEM.端点安全.威胁狩猎.云监控.恶意软件保护等功能. ...
- 见微知著 带你透过内存看 Slice 和 Array的异同
hi, 大家好,我是 hhf. 有这么一个 Go 面试题:请说出 slice 和 array 的区别? 这简直就是送分题.现在思考一下,你咋样回答才能让面试官满意呢? 我这里就不贴这道题的答案了.但是 ...
- GitHub自动化部署(CD) asp.net core 5.0 项目(免费空间)
这里我简单介绍一下使用Github自动化部署自己项目到Heroku云服务器上,Heroku竟然是一个很非常老牌的云平台服务商,竟然还没听说过,网上一查2010被Salesforce收购,网上有很多关于 ...
- 题解 [HNOI2008]GT考试
传送门 这题暴力对拍都难搞,差评 一般的题解里思路是考虑一般DP: 令\(dp[i][j]\)为枚举到第i位时匹配到第j位的方案数,令\(g[k][j]\)为将匹配到k位的情况补到匹配到j位的方案数 ...
- maven打包war,导入本地jar包
方法1: 一 . 在项目根目录创建lib文件夹,把jar放入lib文件夹中 二 . 在项目中使用本地jar pom文件配置如下: <properties> <project.buil ...
- Spark入门:Spark运行架构(Python版)
此文为个人学习笔记如需系统学习请访问http://dblab.xmu.edu.cn/blog/1709-2/ 基本概念 * RDD:是弹性分布式数据集(Resilient Distributed ...
- Spring第一课:核心API(三)
以上是Spring的核心部分,其中需要了解的是:BeanFactory.ApplicationContext[FileSystemXmlApplicationContext.ClassPathXmlA ...
- 关于int和Integer缓存(一):以及设计构想(享元模式)
关于Integer的值缓存:在介绍Integer的值缓存之前,我们需要了解的是,java中的包装类型,我们都知道java中有包装类型int Integer ...