spring注入bean的幾種策略模式


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

本文主題

我們將討論在一個接口或者抽象類在具有多個實現類的情況下,有多少種策略能夠讓我們在特定的代碼片段中注入想要的bean。

按照名稱

定義一個Skill借口,他描述了英雄具備哪些技能,現在有兩個英雄的實現類Diana和Irelia,通過component注解將其標注為bean,在容器啟動的時候會將他們加載到容器中。

現在有一個BannerController要使用Diana這個bean

@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設置默認到的名字也可以自定義,大家自行查找資料了解即可。

@Qualifier 注解

還是上文的例子,如果成員變量名不寫成bean的名稱,是其他的名字,比如按照常規的寫法會寫成

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

@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,那可以把其他的注釋掉

//@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的實現類只有一個,自然就不要再操心注入哪個了,就只會注入存在於容器當中的惟一的那一個了。

使用@Primary提高bean的優先級

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

@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的內置注解,如何來解決我們的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM