文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2024-04-02 19:55

关注

一.引言

我们在使用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注入接口实现类内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯