• SpringBoot根据配置注入接口的不同实现类


    一.引言

    我们在使用springboot进行开发的时候经常用到@Autowired@Resource进行依赖注入,但是当我们一个接口对应多个不同的实现类的时候如果不进行一下配置项目启动时就会报错,那么怎么根据不同的需求注入不同的类型就是一个值得考虑的问题,虽然@Autowired@Resource就可以实现,但是我们也可以选择更加灵活的@ConditionalOnProperty注解来实现

    二.代码演示

    1.问题描述

    TestController.java

    1. package com.example.demo.controller;
    2. import com.example.demo.service.TestService;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.web.bind.annotation.RequestMapping;
    5. import org.springframework.web.bind.annotation.RestController;
    6. /**
    7. * @ClassName TestController
    8. * @Author xuwei
    9. * @DATE 2022/6/28
    10. */
    11. @RestController
    12. @RequestMapping("test")
    13. public class TestController {
    14. //注入需要的service
    15. @Autowired
    16. TestService testService;
    17. @RequestMapping("test")
    18. public void test(){
    19. testService.sayHello();
    20. }
    21. }

     TestService.java

    1. package com.example.demo.service;
    2. /**
    3. * @InterfaceName TestService
    4. * @Author xuwei
    5. * @DATE 2022/6/28
    6. */
    7. public interface TestService {
    8. /**
    9. * sayHello方法
    10. */
    11. void sayHello();
    12. }

    TestService实现类一  TestServiceImplOne.java

    1. package com.example.demo.service.impl;
    2. import com.example.demo.service.TestService;
    3. import org.slf4j.Logger;
    4. import org.slf4j.LoggerFactory;
    5. /**
    6. * @ClassName TestServiceImplOne
    7. * @Author xuwei
    8. * @DATE 2022/6/28
    9. */
    10. @Service
    11. public class TestServiceImplOne implements TestService {
    12. private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplOne.class);
    13. /**
    14. * sayHello方法
    15. */
    16. @Override
    17. public void sayHello() {
    18. LOGGER.info("I am TestServiceImplOne");
    19. }
    20. }

    TestService实现类二 TestServiceImplTwo.java

    1. package com.example.demo.service.impl;
    2. import com.example.demo.service.TestService;
    3. import org.slf4j.Logger;
    4. import org.slf4j.LoggerFactory;
    5. /**
    6. * @ClassName TestServiceImplTwo
    7. * @Author xuwei
    8. * @DATE 2022/6/28
    9. */
    10. @Service
    11. public class TestServiceImplTwo implements TestService {
    12. private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplTwo.class);
    13. /**
    14. * sayHello方法
    15. */
    16. @Override
    17. public void sayHello() {
    18. LOGGER.info("I am TestServiceImplTwo");
    19. }
    20. }

    这时我们的程序启动会报错,大概意思就是找到了两个实现类

    1. ***************************
    2. APPLICATION FAILED TO START
    3. ***************************
    4. Description:
    5. Field testService in com.example.demo.controller.TestController required a single bean, but 2 were found:
    6. - testServiceImplOne: defined in file [/Users/xuwei/Desktop/Projects/IdeaProjects/demo/target/classes/com/example/demo/service/impl/TestServiceImplOne.class]
    7. - testServiceImplTwo: defined in file [/Users/xuwei/Desktop/Projects/IdeaProjects/demo/target/classes/com/example/demo/service/impl/TestServiceImplTwo.class]
    8. Action:
    9. Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

    2.解决方案

    2.1使用@Autowired的时候将接口变量名改为实现类的限定名

    TestController.java修改为如下

    1. package com.example.demo.controller;
    2. import com.example.demo.service.TestService;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.web.bind.annotation.RequestMapping;
    5. import org.springframework.web.bind.annotation.RestController;
    6. /**
    7. * @ClassName TestController
    8. * @Author xuwei
    9. * @DATE 2022/6/28
    10. */
    11. @RestController
    12. @RequestMapping("test")
    13. public class TestController {
    14. //修改变量名为实现类的限定名
    15. @Autowired
    16. TestService testServiceImplOne;
    17. @RequestMapping("test")
    18. public void test(){
    19. testServiceImplOne.sayHello();
    20. }
    21. }

    我们可以将接口的命名改为对应实现类的限定名,默认为类名且首字母小写,当然我们也可以自己给接口的实现类配置限定名,例如@Service("serviceOne") 之后在引用时使用我们配置的限定名,这样程序都可以自动找到实现类,测试结果如下:

    2.2 使用@Autowired配合@Qualifier指定限定名注入实现类

    其实这个方法的原理和上面的很相似,@Autowired会默认根据type进行注入,如果type相同会根据id进行注入,也就是我们说的限定名,我们只需要让它找到对应限定名的类即可,上面我们通过修改接口变量名的方式来实现,同时我们还可以配合@Qualifier注解来实现相同的目的

    TestController.java修改为如下

    1. package com.example.demo.controller;
    2. import com.example.demo.service.TestService;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.beans.factory.annotation.Qualifier;
    5. import org.springframework.web.bind.annotation.RequestMapping;
    6. import org.springframework.web.bind.annotation.RestController;
    7. /**
    8. * @ClassName TestController
    9. * @Author xuwei
    10. * @DATE 2022/6/28
    11. */
    12. @RestController
    13. @RequestMapping("test")
    14. public class TestController {
    15. //配合注解指定限定名
    16. @Qualifier("testServiceImplTwo")
    17. @Autowired
    18. TestService testService;
    19. @RequestMapping("test")
    20. public void test(){
    21. testService.sayHello();
    22. }
    23. }

    当然,和上一种方法相同,我们注解中填的值是实现类的限定名,可以使用默认,也可以和上面一样在使用@Service时进行配置,测试结果如下:

    2.3@ConditionalOnProperty

    以上两种方法都是硬编码方式,在我们需要进行用户配置时很不方便,所以我们可以使用@ConditionalOnProperty注解来实现配置文件控制的功能

    在TestController中使用@Resource注入

    1. package com.example.demo.controller;
    2. import com.example.demo.service.TestService;
    3. import org.springframework.web.bind.annotation.RequestMapping;
    4. import org.springframework.web.bind.annotation.RestController;
    5. import javax.annotation.Resource;
    6. /**
    7. * @ClassName TestController
    8. * @Author xuwei
    9. * @DATE 2022/6/28
    10. */
    11. @RestController
    12. @RequestMapping("test")
    13. public class TestController {
    14. //使用@Resource注入
    15. @Resource
    16. TestService testService;
    17. @RequestMapping("test")
    18. public void test(){
    19. testService.sayHello();
    20. }
    21. }

    TestServiceImplOne.java

    1. package com.example.demo.service.impl;
    2. import com.example.demo.service.TestService;
    3. import org.slf4j.Logger;
    4. import org.slf4j.LoggerFactory;
    5. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    6. import org.springframework.stereotype.Component;
    7. /**
    8. * @ClassName TestServiceImplOne
    9. * @Author xuwei
    10. * @DATE 2022/6/28
    11. */
    12. @Component
    13. @ConditionalOnProperty(name = "serviceControl",havingValue = "serviceOne")
    14. public class TestServiceImplOne implements TestService {
    15. private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplOne.class);
    16. /**
    17. * sayHello方法
    18. */
    19. @Override
    20. public void sayHello() {
    21. LOGGER.info("I am TestServiceImplOne");
    22. }
    23. }

    TestServiceImplTwo.java

    1. package com.example.demo.service.impl;
    2. import com.example.demo.service.TestService;
    3. import org.slf4j.Logger;
    4. import org.slf4j.LoggerFactory;
    5. import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    6. import org.springframework.stereotype.Component;
    7. /**
    8. * @ClassName TestServiceImplTwo
    9. * @Author xuwei
    10. * @DATE 2022/6/28
    11. */
    12. @Component
    13. @ConditionalOnProperty(name = "serviceControl",havingValue = "serviceTwo")
    14. public class TestServiceImplTwo implements TestService {
    15. private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplTwo.class);
    16. /**
    17. * sayHello方法
    18. */
    19. @Override
    20. public void sayHello() {
    21. LOGGER.info("I am TestServiceImplTwo");
    22. }
    23. }

    在配置文件中配置我们使用的类

    测试结果如下 

     三.总结

    前两种方法都是去寻找接口的限定名,第三种方法中@ConditionalOnProperty(name = "serviceControl",havingValue = "serviceOne")注解的name属性对应配置文件中的key值,而havingValue属性对应的是配置文件中我们上面定义的name属性对应的value值

  • 相关阅读:
    学校官网首页 2页网页设计(HTML+CSS+JavaScript)
    网络是怎样连接的--DNS服务器查询原理
    java集合框架------Collection接口
    idea springboot 自定义注释无效
    DSPE-PEG-cRGD,磷脂-聚乙二醇-靶向穿膜肽cRGD,一种靶向功能材料
    Java获取AD域内所有用户
    Java特性之设计模式【组合模式】
    Python深度学习之路:TensorFlow与PyTorch对比【第140篇—Python实现】
    头歌——Linux——下的 c 编程
    SpringMVC异常处理器
  • 原文地址:https://blog.csdn.net/qq_16469323/article/details/125502523