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

2022-11-13 11:11:15 接口 注入 演示

一.引言

我们在使用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;
 

@RestController
@RequestMapping("test")
public class TestController {
 
    //注入需要的service
    @Autowired
    TestService testService;
 
    @RequestMapping("test")
    public void test(){
        testService.sayHello();
    }
}

 TestService.java

package com.example.demo.service;
 

public interface TestService {
    
    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;
 

@Service
public class TestServiceImplOne implements TestService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplOne.class);
    
    @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;
 

@Service
public class TestServiceImplTwo implements TestService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplTwo.class);
    
    @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;
 

@RestController
@RequestMapping("test")
public class TestController {
 
    //修改变量名为实现类的限定名
    @Autowired
    TestService testServiceImplOne;
 
    @RequestMapping("test")
    public void test(){
        testServiceImplOne.sayHello();
    }
}

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

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;
 

@RestController
@RequestMapping("test")
public class TestController {
 
    //配合注解指定限定名
    @Qualifier("testServiceImplTwo")
    @Autowired
    TestService testService;
 
    @RequestMapping("test")
    public void test(){
        testService.sayHello();
    }
}

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

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;
 

@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;
 

@Component
@ConditionalOnProperty(name = "serviceControl",havingValue = "serviceOne")
public class TestServiceImplOne implements TestService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplOne.class);
    
    @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;
 

@Component
@ConditionalOnProperty(name = "serviceControl",havingValue = "serviceTwo")
public class TestServiceImplTwo implements TestService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImplTwo.class);
    
    @Override
    public void sayHello() {
        LOGGER.info("I am TestServiceImplTwo");
    }
}

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

测试结果如下

 三.总结

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

到此这篇关于SpringBoot根据配置注入接口的不同实现类的文章就介绍到这了,更多相关SpringBoot注入接口实现类内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章