Spring核心——註解自動裝載

全文共 2478 個字html

從配置上擴展

以前的文章介紹了Spring的IoC容器配置管理方面的詳細內容,須要瞭解的能夠從IoC容器的設計模式開始閱讀。在介紹基於註解配置的配置以前咱們再重複一下在以前提到的基本認識:java

  1. Spring的基本工做單位是Bean,全部的高級功能都是在Bean的基礎上擴展而來的。Bean能夠理解成Java類的一個實例。
  2. Bean只是一個個體,Spring用一個名爲IoC(Inversion of Control控制反轉)的容器來管理全部的Bean。
  3. Spring的核心功能就是管理Bean與Bean之間、IoC容器與Bean之間的依賴、組合關係。這些關係經過XML配置來定義。

基於以上3點,對XML配置有清晰的理解對Spring核心框架的使用相當重要。在Spring沒有註解(Annotation)以前,咱們都是經過XML配置來實現Spring的功能,而在3.x版本以後,咱們能夠僅使用註解而無需XML便可運行Spring。註解並無擴展Spring的核心功能,他僅僅是將原來XML上的配置遷移到Java源碼中以「元數據」(bytecode metadata)的方式提供非侵入式(non-invasive)的框架服務。因此不管XML配置仍是註解的方式,或者二者混合使用均可以實現Spring提供的全部功能。spring

在以前IOC功能擴展點一文中介紹了BeanDefinition的適配器模式。以下圖,瞭解BeanDefinition的做用以後就能明白不管是註解仍是XML配置,最後都會轉化爲一個BeanDefinition結構讓IoC容器來執行初始化。設計模式

Spring核心——註解自動裝載

Spring的註解相關的功能是在2.x版本開始出現而後到3.x才所有完善的,支持JCP制定的JSR-250和JSR-330。因此在使用註解的時候須要注意版本號。api

啓用Annotation配置功能

在使用註解功能以前要告訴IoC如今須要啓用註解相關的功能,經過上下文級別的配置便可開啓全部註解相關的功能:框架

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

官配後置處理器一文已經介紹了註解的處理都是經過後置處理器實現的,因此添加了<context:annotation-config/>這個配置以後在實現層面會向IoC容器增長AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessorRequiredAnnotationBeanPostProcessor這幾個後設置處理器。每一個處理器都針對某一個或者某些註解進行處理。maven

還有,若是在工程中混合使用註解和XML配置,若是同一個Bean同時在XML和註解都進行了配置,那麼最終生效的是XML上的配置,由於Spring容器會先處理註解再處理XML配置。工具

下面是關於自動裝載的註解介紹:post

@Autowired

這個註解應該是使用spring最經常使用的註解,也是IoC容器反向依賴注入的極致體現。基本上容器裏有什麼實現咱們根本沒必要操心,之須要聲明一個接口加一個@Autowired就能夠得到對應的接口功能。若是不使用參數的話@Autowired的效果就至關於BeanFactory.getBean(Class<T> )。ui

多種方法注入數據

@Autowired能夠直接寫在域(成員變量)上、能夠用在通常的方法和構造方法上:

interface A {}
interface B {}
interface C {}

class MyClass {
	@Autowired //從成員變量注入
	private A a;
	private B b;
	private C c;

	@Autowired //從構造方法注入
	public MyClass(C c) {
		this.c = c;
	}
	public A getA() {
		return a;
	}
	public void setA(A a) {
		this.a = a;
	}
	public B getB() {
		return b;
	}
	@Autowired //從普通方法注入
	public void setB(B b) {
		this.b = b;
	}
	public C getC() {
		return c;
	}
	public void setC(C c) {
		this.c = c;
	}
}

獲取同一個接口的多個實現

若是容器中同一個接口有相同的實現,咱們能夠用數據、列表或Map結構來獲取多個Bean:

interface A {}
class implA1 implements A{}
class implA2 implements A{}

class MyClass {
	@Autowired
	private A[] a;
	@Autowired
	private Set<A> set;
	@Autowired
	private Map<String, A> map;
}

使用Map時,key必須聲明爲String,在運行時會key是注入Bean的name/id。

聲明非必要數據

當咱們使用@Autowired時,若是容器中沒有咱們所需的Bean會拋出異常。當這個Bean並非必要數據時可使用@Autowired的required參數:

interface A {}
class MyClass {
	@Autowired(required=false)
	private A a;
}

自動空指針處理

在Java8以後專門爲空指針處理添加了Optional這個工具類。在4.x以後Spring在注入數據階段會根據目標對象自動進行包裝:

interface A {}
class impl implements A{}
class MyClass {
	@Autowired(required=false)
	private Optional<A> a;
}

這個時候在容器中存放的是接口A的實現類,可是會自定根據注入對象的聲明新建一個Optional包裝的Bean。

在5.x版本以後還可使用JSR-305提出的@NullAble告訴IoC這裏能夠注入一個空指針數據或什麼也不須要。

interface A {}
class impl implements A{}
class MyClass {
	private A a;
	public void setA(@Nullable A a) {
		this.a = a;
	}
}

獲取容器中的全部資源

除了咱們本身聲明的接口、類,@Autowired還能夠獲取Spring定義的全部Bean,凡是隻要在IoC容器中的Bean均可以經過它來獲取:

class MyClass {
	@Autowired
	private ApplicationContext context;
}

不過須要注意的是,這些註解(Annotation)支持的功能都是由後置處理器實現的,因此沒法自動注入後置處理器(BeanPostProcessor或BeanFactoryPostProcessor )。

JSR-330支持

JSR-330提出了反向依賴注入的相關內容,主要是關於@Inject、@ManagedBean、@Singleton的做用和實現方式。使用@Inject能夠替換@Autowired的絕大部分功能,可是還有些許的差別。使用JSR-330要引入javax.inject包,maven的配置以下:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

而後能夠用@Inject註解任何使用@Autowired的地方,惟一的區別是@Inject沒有required參數,若是要實現對應的功能只能經過注入Optional<Class<T>>的方式實現。

對自動裝配的控制

@Autowired雖然好用,可是也會遇到一些問題,好比當容器中有2個類實現同一個接口的時候在運行時注入就會拋出異常,針對這個問題Spring提供了一些針對自動裝配更細節的操做——Primary和Qualifiers。

Primary控制自動裝配

Primary字面意思就是主要的,意思是告訴容器這個Bean是「主」Bean。實現Primary有兩種方式,經過在@Configuration中註解實現或在XML配置中實現。

首先是配置方式。有一個接口A、有2個A的實現類ImplFirst和ImplSecond,而後在功能類MyClass中自動裝配了接口A:

interface A {}
class ImplFirst implements A{}
class ImplSecond implements A{}
class MyClass {
	@Autowired
	private A a;
}

在配置文件中咱們可使用primary屬性來控制注入哪個實現類。

<beans>
    <context:annotation-config/>
    <!-- 自動裝配時會使用這個Bean -->
    <bean class="x.y.ImplFirst" primary="true"/>
    <bean class="x.y.ImplSecond"/>
    <bean class="x.y.MyClass">
</beans>

此外,咱們還但是使用Java配置功能(@Configuration相關的Java配置內容後續篇幅會介紹)指明「主」Bean。將XML配置文件替換爲下面的Java配置形式:

@Configuration
class MyClassConfig {
	@Bean
	@Primary
	public A firstImpl() {
		return new ImplFirst();
	}
	@Bean
	public A secondImpl() {
		return new ImplSecond();
	}
}

Qualifiers控制自動裝配

Primary是類的實現者決定使用那個一個類,而Qualifiers是讓類的使用者來肯定使用哪個類。先看一個最基本的用法:

interface A {}
class ImplFirst implements A {}
class ImplSecond implements A {}
class MyClass {
	@Autowired
	@Qualifier("implSecond")
	private A a;
}
<beans>
    <context:annotation-config/>
    <bean id="implFirst" class="x.y.ImplFirst"/>
    <bean id="implSecond" class="x.y.ImplSecond"/>
    <bean class="x.y.MyClass">
</beans>

這樣經過@Qualifiers註解就制定加載了ImplFIrst這個類。不過Spring官方並不建議這樣去使用Qualifiers。主要的緣由是失去了@Autowired的使用初衷——在使用的時候還須要去了解<bean>的定義結構。官方建議經過別名的形式告知每個類的做用,而後經過Qualifiers來使用。例如咱們仍是用組裝電腦的例子:

interface Cpu {}
class Athlon implements Cpu {
	private String brand = "Amd";
}
class Celeron implements Cpu {
	private String brand = "Intel";
}

class MyPc {
	@Autowired
	@Qualifier("Intel")
	private Cpu a;
}

class YourPc {
	@Autowired
	@Qualifier("Amd")
	private Cpu a;
}
<beans>
    <context:annotation-config/>
    <bean id="athlon" class="x.y.Athlon">
        <qualifier value="Amd"/>
    </bean>
    <bean id="celeron" class="x.y.Celeron">
        <qualifier value="Intel"/>
    </bean>
    <bean class="x.y.MyPc"/>
    <bean class="x.y.YourPc"/>
</beans>

<qualifier value="Intel"/>能夠聲名當前的Bean對應的Qualifier的名稱。這樣徹底就將Id和Qualifer定義的名稱隔離開,咱們可使用規範來約定使用的功能內容。

還能夠經過繼承@Qualifier來實現咱們更細節的控制和管理,咱們將上面的代碼進行以下修改:

@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@interface CpuType {
	String value();
}
interface Cpu {
}
class Athlon implements Cpu {
	private String brand = "Amd";
}
class Celeron implements Cpu {
	private String brand = "Intel";
}
class MyPc {
	@Autowired
	@CpuType("Intel")
	private Cpu a;
}
class YourPc {
	@Autowired
	@CpuType("Amd")
	private Cpu a;
}
<beans>
    <context:annotation-config/>
    <bean id="athlon" class="x.y.Athlon">
        <qualifier type="CpuType" value="Amd"/>
    </bean>
    <bean id="celeron" class="x.y.Celeron">
        <qualifier type="x.y.CpuType" value="Intel"/>
    </bean>
    <bean class="x.y.MyPc"/>
    <bean class="x.y.YourPc"/>
</beans>

在<qualifier>中type指向的註解類能夠是全限定名,也能夠用短編碼。

咱們還能夠在繼承的註解中使用自定義參數。例如:

@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@interface CpuType {
	String brand();
    String ver();
}

配置:

<beans>
    <context:annotation-config/>
    <bean id="athlon" class="x.y.Athlon">
        <qualifier type="CpuType">
            <attribute key="brand" value="Amd"/>
            <attribute key="ver" value="4321"/>
        </qualifier>
    </bean>
    <bean class="x.y.MyPc"/>
</beans>
相關文章
相關標籤/搜索