spring注入bean的幾種策略模式

上篇文章Spring IOC的核心機制:實例化與注入咱們提到在有多個實現類的狀況下,spring是如何選擇特定的bean將其注入到代碼片斷中,咱們討論了按照名稱注入和使用@Qualifier 註解輸入的兩種方式,本篇文章將結合以前提到的和spring的其餘注入方式一塊兒進行討論。html

本文主題

咱們將討論在一個接口或者抽象類在具備多個實現類的狀況下,有多少種策略可以讓咱們在特定的代碼片斷中注入想要的bean。java

按照名稱

定義一個Skill藉口,他描述了英雄具有哪些技能,如今有兩個英雄的實現類Diana和Irelia,經過component註解將其標註爲bean,在容器啓動的時候會將他們加載到容器中。spring

如今有一個BannerController要使用Diana這個beanapp

@RestController
public class BannerController {

    @Autowired
    private Skill diana;

    @RequestMapping(value = "/v2/banner", method = {RequestMethod.GET})
    public String test() {
        diana.r();
        return "Hello SpringBoot";
    }
}

private Skill diana;這裏將成員變量的名字寫成Diana這個bean的名字,容器就會選擇Diana這個注入,這就叫作按照名稱注入。spring會給每一個bean設置默認到的名字也能夠自定義,你們自行查找資料瞭解便可。ide

@Qualifier 註解

仍是上文的例子,若是成員變量名不寫成bean的名稱,是其餘的名字,好比按照常規的寫法會寫成測試

private Skill skill;這個時候就可使用@Qualifier 註解,this

@RestController
public class BannerController {

    @Autowired
    @Qualifier("diana")
    private Skill skill;

    @RequestMapping(value = "/v2/banner", method = {RequestMethod.GET})
    public String test() {
        skill.r();
        return "Hello SpringBoot";
    }

有選擇的注入一個bean,註釋掉某個bean上的@Component

若是咱們肯定要使用哪一個bean,那能夠把其餘的註釋掉code

//@Component
public class Irelia implements Skill {

    public Irelia() {
        System.out.println("Hello, Irelia");
    }
    public void q(){
        System.out.println("Irelia Q");
    }

    public void w(){
        System.out.println("Irelia W");
    }

    public void e(){
        System.out.println("Irelia E");
    }

    public void r(){
        System.out.println("Irelia R");
    }
}

讓Skill的實現類只有一個,天然就不要再操心注入哪一個了,就只會注入存在於容器當中的唯一的那一個了。component

使用@Primary提升bean的優先級

還可使用@Primary註解提升咱們想讓注入bean的優先級,那spring在掃描到相同類型的bean的時候,就會看誰的優先級高,誰高誰注入htm

@Component
@Primary
public class Diana implements Skill {
    private String skillName = "Diana R";

    public Diana() {
        System.out.println("I am Diana");
    }
    public void q(){
        System.out.println("Diana Q");
    }

    public void w(){
        System.out.println("Diana W");
    }

    public void e(){
        System.out.println("Diana E");
    }

    public void r(){
        System.out.println(this.skillName);
    }
}

而後啓動啓動程序,訪問路由,就會看到下面的輸出:

image-20200505154939833

說明確實是注入了Diana這個bean。

階段總結

上面的實現方式看起來都能實現按照所需注入特定的bean,可是有個問題,當發生變化的時候,這裏的變化多是英雄名變了,新增英雄或者要替換其餘英雄,都須要去手動改代碼,有些狀況下可能還不僅改一處,仍是比較繁瑣,並不方便。那有沒有一種方式能夠經過配置文件的形式來指定要注入的bean,當變化發生,只須要改配置,那豈不是很方便很靈活很開心。

條件註解

條件註解顧名思義,就是按照條件決定注入哪個bean,因此要想使用條件註解,就得先寫一個條件。

spring爲咱們提供了特定的方式來實現本身的條件:@Conditional註解+Condition接口,spring的這種方式的原理是把符合條件的bean加加載到容器中,不合的不加載,那這樣在注入的時候就不會有多個相同類型的bean存在了,天然也就注入成功了。

定義一個類實現Condition接口

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class HeroCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  
        return false;
    }
}

實現Condition接口必須實現它的matches方法,咱們先來解釋一下這個方法的入參和返回值,

ConditionContext是一個接口,定義了他支持那些操做

public interface ConditionContext {
	BeanDefinitionRegistry getRegistry();
	ConfigurableListableBeanFactory getBeanFactory();
	Environment getEnvironment();
	ResourceLoader getResourceLoader();
	ClassLoader getClassLoader();
}

其中的getEnvironment方法會返回Environment對象,他裏面會記錄當前應用全部的環境信息,能夠經過這個對象獲取到咱們的配置信息。

返回值是Boolean類型的,哪一個bean的條件匹配成功,就會把這個bean注入到代碼片斷中去。咱們來具體實現一下。

public class HeroCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String name = context.getEnvironment().getProperty("hero.condition");
        System.out.println(name);
        if ("diana".equalsIgnoreCase(name)){
            return true;
        }else if ("irelia".equalsIgnoreCase(name)){
            return true;
        }
        return false;
    }
}
@Configuration
public class HeroConfiguration {
    @Bean
    @Conditional(HeroCondition.class)
    public Skill diana(){
        return new Diana();
    }

    @Bean
    @Conditional(HeroCondition.class)
    public Skill irelia(){
        return new Irelia();
    }
  
}

經過以上的方式,就能夠完成一個簡單的條件註解,可是這種需求其實spring boot早已經幫咱們實現了,提供了一些註解可使用,下篇文章咱們看看使用spring boot的內置註解,如何來解決咱們的問題。


博客地址:https://www.immortalp.com/

歡迎你們去 個人博客 瞅瞅,裏面有更多關於測試實戰的內容哦!!

相關文章
相關標籤/搜索