目录
  • 一.引言
  • 二.代码演示
    • 1.问题描述
    • 2.解决方案
      • 2.1使用@Autowired的时候将接口变量名改为实现类的限定名
      • 2.2 使用@Autowired配合@Qualifier指定限定名注入实现类
      • 2.3@ConditionalOnProperty
  •  三.总结

    一.引言

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

    二.代码演示

    1.问题描述

    TestController.java

    package com.example.demo.controller;
     
    import com.example.demo.service.TestService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    /**
     * @ClassName TestController
     * @Author xuwei
     * @DATE 2022/6/28
     */
    @RestController
    @RequestMapping("test")
    public class TestController {
     
        //注入需要的service
        @Autowired
        TestService testService;
     
        @RequestMapping("test")
        public void test(){
            testService.sayHello();
        }
    }

     TestService.java

    package com.example.demo.service;
     
    /**
     * @InterfaceName TestService
     * @Author xuwei
     * @DATE 2022/6/28
     */
    public interface TestService {
        /**
         * sayHello方法
         */
        void sayHello();
    }

    TestService实现类一  TestServiceImplOne.java

    package com.example.demo.service.impl;
     
    import com.example.demo.service.TestService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    /**
     * @ClassName TestServiceImplOne
     * @Author xuwei
     * @DATE 2022/6/28
     */
    @Service
    public class TestServiceImplOne implements TestService {
        private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplOne.class);
        /**
         * sayHello方法
         */
        @Override
        public void sayHello() {
            LOGGER.info("I am TestServiceImplOne");
        }
    }

    TestService实现类二 TestServiceImplTwo.java

    package com.example.demo.service.impl;
     
    import com.example.demo.service.TestService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    /**
     * @ClassName TestServiceImplTwo
     * @Author xuwei
     * @DATE 2022/6/28
     */
    @Service
    public class TestServiceImplTwo implements TestService {
        private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplTwo.class);
        /**
         * sayHello方法
         */
        @Override
        public void sayHello() {
            LOGGER.info("I am TestServiceImplTwo");
        }
    }

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

    ***************************
    APPLICATION FAILED TO START
    ***************************
    Description:
    Field testService in com.example.demo.controller.TestController required a single bean, but 2 were found:
        – testServiceImplOne: defined in file [/Users/xuwei/Desktop/Projects/IdeaProjects/demo/target/classes/com/example/demo/service/impl/TestServiceImplOne.class]
        – testServiceImplTwo: defined in file [/Users/xuwei/Desktop/Projects/IdeaProjects/demo/target/classes/com/example/demo/service/impl/TestServiceImplTwo.class]
    Action:
    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修改为如下

    package com.example.demo.controller;
     
    import com.example.demo.service.TestService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    /**
     * @ClassName TestController
     * @Author xuwei
     * @DATE 2022/6/28
     */
    @RestController
    @RequestMapping("test")
    public class TestController {
     
        //修改变量名为实现类的限定名
        @Autowired
        TestService testServiceImplOne;
     
        @RequestMapping("test")
        public void test(){
            testServiceImplOne.sayHello();
        }
    }

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

    使用SpringBoot根据配置注入接口的不同实现类(代码演示)

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

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

    TestController.java修改为如下

    package com.example.demo.controller;
     
    import com.example.demo.service.TestService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    /**
     * @ClassName TestController
     * @Author xuwei
     * @DATE 2022/6/28
     */
    @RestController
    @RequestMapping("test")
    public class TestController {
     
        //配合注解指定限定名
        @Qualifier("testServiceImplTwo")
        @Autowired
        TestService testService;
     
        @RequestMapping("test")
        public void test(){
            testService.sayHello();
        }
    }

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

    使用SpringBoot根据配置注入接口的不同实现类(代码演示)

    2.3@ConditionalOnProperty

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

    在TestController中使用@Resource注入

    package com.example.demo.controller;
     
    import com.example.demo.service.TestService;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    import javax.annotation.Resource;
     
    /**
     * @ClassName TestController
     * @Author xuwei
     * @DATE 2022/6/28
     */
    @RestController
    @RequestMapping("test")
    public class TestController {
     
        //使用@Resource注入
        @Resource
        TestService testService;
     
        @RequestMapping("test")
        public void test(){
            testService.sayHello();
        }
    }

    TestServiceImplOne.java

    package com.example.demo.service.impl;
     
    import com.example.demo.service.TestService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.stereotype.Component;
     
    /**
     * @ClassName TestServiceImplOne
     * @Author xuwei
     * @DATE 2022/6/28
     */
    @Component
    @ConditionalOnProperty(name = "serviceControl",havingValue = "serviceOne")
    public class TestServiceImplOne implements TestService {
        private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplOne.class);
        /**
         * sayHello方法
         */
        @Override
        public void sayHello() {
            LOGGER.info("I am TestServiceImplOne");
        }
    }

    TestServiceImplTwo.java

    package com.example.demo.service.impl;
     
    import com.example.demo.service.TestService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.stereotype.Component;
     
    /**
     * @ClassName TestServiceImplTwo
     * @Author xuwei
     * @DATE 2022/6/28
     */
    @Component
    @ConditionalOnProperty(name = "serviceControl",havingValue = "serviceTwo")
    public class TestServiceImplTwo implements TestService {
        private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplTwo.class);
        /**
         * sayHello方法
         */
        @Override
        public void sayHello() {
            LOGGER.info("I am TestServiceImplTwo");
        }
    }

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

    使用SpringBoot根据配置注入接口的不同实现类(代码演示)

    测试结果如下

    使用SpringBoot根据配置注入接口的不同实现类(代码演示)

     三.总结

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

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。