一、问题的形容
在理论的零碎利用开发中我常常会遇到这样的一类需要,置信大家在工作中也会常常遇到:
- 同一个零碎在多个省份部署。
- 一个业务在北京是一种实现形式,是基于北京用户的需要。
- 同样的业务在上海是另外一种实现形式,与北京的实现形式大同小异
遇到这样的需要,咱们通常会定义一个业务实现的接口,比方:
public interface IDemoService {public void doSomething();
}
在北京环境下这样实现,比方:
@Component
public class DemoServiceBeijing implements IDemoService {
@Override
public void doSomething() {System.out.println("北京的业务实现");}
}
在上海环境下这样实现,比方:
@Component
public class DemoServiceShanghai implements IDemoService {
@Override
public void doSomething() {System.out.println("上海的业务实现");}
}
而后咱们写一个模仿业务测试用例
@SpringBootTest
class DemoApplicationTests {
// 这里注入的 demoService 是 DemoServiceShanghai,还是 DemoServiceBeijing?@Resource
IDemoService demoService;
@Test
void testDemoService() {demoService.doSomething();
}
}
当咱们执行这个测试用例的时候肯定会报错,因为 Spring 发现了两个 IDemoService 的实现类。它不晓得去实例化哪一个实现类,来作为 IDemoService 的理论业务解决 bean。当然咱们冀望的状态是:
- 在北京部署零碎的时候,应用 DemoServiceBeijing 作为 IDemoService 的实现类实现依赖注入
- 在上海部署零碎的时候,应用 DemoServiceShanghai 作为 IDemoService 的实现类实现依赖注入
二、绝对低级解决方案
面对下面的需要,先说几个绝对低级的解决方案,这几个计划尽管能够实现咱们冀望的状态,然而对运维不够敌对。
2.1. 计划一:应用 @Primary
注解
如果在北京部署零碎的时候,在 DemoServiceBeijing 的类下面加上@Primary
, 该注解的作用就是强制从多个实现类外面选一个实现类,如果 Spring 不晓得选哪一个,咱们通知它一个默认的。
@Primary
@Component
public class DemoServiceBeijing implements IDemoService {
2.2. 计划二:应用 @Resource
注解
因为 Resource 注解默认应用名称进行依赖注入,所以变量名明确叫做 demoServiceBeijing(首字母小写),应用的就是 DemoServiceBeijing 实现类。
@Resource
IDemoService demoServiceBeijing; // 这里的变量名称指定了 bean 名称
//IDemoService demoService; 被替换掉
或者
@Resource(name = "demoServiceBeijing") // 应用 resource 注解明确指定名称
IDemoService demoService;
2.3. 计划三:应用 @Qualifier
注解
与上文同样的情理,应用 @Qualifier
注解,指定 bean 的名称进行依赖注入
@Qualifier("demoServiceBeijing") // 应用 Qualifier 注解明确指定名称
@Resource
IDemoService demoService;
下面所提到的三个计划尽管都能够解决:在不同的部署环境下应用不同的接口实现类实现依赖注入的问题。然而这样不好,因为一旦咱们要把部署环境从 beijing(北京)换成 shanghai(上海),就须要把下面的注解的地位或者内容全都批改一遍(所有的实现类代码都要批改)。
三、绝对高级的解决方案
咱们提出进一步的冀望:就是只批改一个配置就能实现部署环境切换的操作。比方:
deploy:
province: beijing
当咱们冀望把部署环境从北京切换到上海的时候,只须要将上文配置中的 beijing 改成 shanghai,这该怎么实现呢?
- 在北京的实现类下面加上 ConditionalOnProperty 注解,havingValue 的值为 beijing
@Component
@ConditionalOnProperty(value="deploy.province",havingValue = "beijing")
public class DemoServiceBeijing implements IDemoService {
- 在上海的实现类下面加上 ConditionalOnProperty 注解,havingValue 的值为 shanghai
@Component
@ConditionalOnProperty(value="deploy.province",havingValue = "shanghai")
public class DemoServiceShanghai implements IDemoService {
ConditionalOnProperty 注解在这里的作用就是 :读取配置文件发现deploy.province
, 并将该配置的值与 havingValue 匹配,匹配上哪一个就实例化哪一个类作为该接口的实现类 bean 注入到 Spring 容器中(当然注入过程须要配合@Component
注解实现)。
欢送关注我的布告号:字母哥杂谈,回复 003 赠送作者专栏《docker 修炼之道》的 PDF 版本,30 余篇精品 docker 文章。字母哥博客:zimug.com