一、引言

在开发中经常会碰到这样的情形,一个接口会有不同的实现,但在开发中都是基于接口的注入,那么怎么根据不同的需求注入不同的类型就是一个值得考虑的问题。在注入属性时常用的两个注解是@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注解可以实现根据配置文件中的值注入不同的实现类。

最新文章

  1. How To Restart timer service on all servers in farm
  2. 模块(modue)的概念:
  3. [ACM_水题] ZOJ 3712 [Hard to Play 300 100 50 最大最小]
  4. Spark编译安装和运行
  5. 从QQ网站中提取的纯JS省市区三级联动
  6. bedtools 每天都会用到的工具
  7. JavaScript DOM高级程序设计1.2-循序最佳实践--我要坚持到底!
  8. spring-quartz普通任务与可传参任务
  9. 系统学下POWERSHELL吧,工作当中可能用得到呢。不能像以前那样修修改改了。
  10. Android应用开发基础篇(14)-----自定义标题栏
  11. 蓝桥网试题 java 入门训练 圆的面积
  12. 2017-3-2 C# WindowsForm 中label标签居中显示
  13. Java中设计模式之生产者消费者模式-4
  14. 并发系列(6)之 ThreadPoolExecutor 详解
  15. hihoCoder 1033: 交错和
  16. [Module] 06 - DataBinding and MVVM
  17. 在CentOS6.8下安装Docker
  18. python dict 构造函数性能比较
  19. web项目在eclipse workspace中的位置
  20. qt 使用qtxlsx 读写excel

热门文章

  1. Install Redmine Server with Bitnami Installer
  2. Required request body is missing-请求接口报错
  3. DVWA(九):File Upload 全等级文件上传
  4. (数据科学学习手札127)在Python中使用icecream实现高效debug
  5. SQL 练习5
  6. 接口和包--Java学习笔记
  7. "image watch" for QtCreator
  8. webapp网络定位
  9. mybatis传入参数为0被误认为是空字符串的解决方法
  10. Linux中增加组和用户