首頁 > 軟體

使用SpringBoot根據設定注入介面的不同實現類(程式碼演示)

2022-06-30 18:00:54

一.引言

我們在使用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") 之後在參照時使用我們設定的限定名,這樣程式都可以自動找到實現類,測試結果如下:

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時進行設定,測試結果如下:

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");
    }
}

在組態檔中設定我們使用的類

測試結果如下

 三.總結

前兩種方法都是去尋找介面的限定名,第三種方法中@ConditionalOnProperty(name = "serviceControl",havingValue = "serviceOne")註解的name屬性對應組態檔中的key值,而havingValue屬性對應的是組態檔中我們上面定義的name屬性對應的value值

到此這篇關於SpringBoot根據設定注入介面的不同實現類的文章就介紹到這了,更多相關SpringBoot注入介面實現類內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com