天天看點

淺析Spring中的注解

    spring的配置,通常有兩種:使用配置檔案和注解。那麼spring如何知道各個bean或者service、controller以及bean中各類屬性之間的關系呢?答案肯定是在定義各個java檔案的時候使用了各種注解,它們交織在一起,實作了使用配置檔案完成的配置功能。

一、bean相關的注解

    與springbean相關的注解有以下四大類:

@component:标注一個普通的spring bean類

@controller:标注一個控制器元件類

@service:标注一個業務邏輯元件類

@repository:标注一個dao元件類

    如果我們需要定義一個普通的spring bean,那麼直接使用@component标注即可。但如果用@repository、@service或者@controller來标注,那麼這個bean類将被作為特殊的javaee元件來對待。在spring的未來版本中,@controller、@service和@repository也許還能攜帶更多的語義,是以,如果需要在javaee應用中使用這些注解時,盡量考慮使用@controller、@service和@repository來代替普通的@component注解。例如:

<a href="http://my.oschina.net/itblog/blog/207353#">?</a>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

<code>@scope</code><code>(</code><code>"prototype"</code><code>)</code>

<code>@component</code><code>(</code><code>"pp"</code><code>)</code>

<code>public</code> <code>class</code> <code>people {</code>

<code>    </code><code>private</code> <code>int</code> <code>age;</code>

<code>    </code><code>private</code> <code>string name;</code>

<code>    </code><code>//省略getter和setter</code>

<code>}</code>

<code>@service</code>

<code>public</code> <code>class</code> <code>peopleservice {</code>

<code>    </code><code>@autowired</code>

<code>    </code><code>private</code> <code>peoplerepo peopledao;</code>

<code>    </code> 

<code>    </code><code>public</code> <code>void</code> <code>addpeople(people p) {</code>

<code>        </code><code>//other business logic here</code>

<code>        </code><code>//...call method in peoplerepo to complete</code>

<code>    </code><code>}</code>

<code>    </code><code>//省略 peopledao的setter和getter</code>

<code>@repository</code>

<code>public</code> <code>class</code> <code>peoplerepo {</code>

<code>    </code><code>public</code> <code>void</code> <code>addpeople(peopleentity p) {</code>

<code>        </code><code>//...insert data only</code>

    指定了某些類可以作為spring bean後,還需要為spring指定bean的搜尋路徑,便于spring自動在這個路徑下搜尋相關的bean。在指定這個(或這些,如果有多個的話)路徑之前,需要在spring配置檔案中導入context schema:

<code>&lt;!-- 第4,6,7行是用于導入context schema的 --&gt;</code>

<code>&lt;</code><code>beans</code> <code>xmlns=" </code>

<code>    </code><code>xmlns:xsi</code><code>=</code><code>"http://www.w3.org/2001/xmlschema-instance"</code>

<code>    </code><code>xmlns:context</code><code>=</code><code>"http://www.springframework.org/schema/context"</code>

<code>    </code><code>xsi:schemalocation</code><code>=</code><code>"http://www.springframework.org/schema/beans"</code>

<code>    </code><code>http://www.springframework.org/schema/context</code>

<code>    </code><code>http://www.springframework.org/schema/context/spring-context-3.0.xsd"&gt;</code>

<code>    </code><code>&lt;!-- 指定spring将要掃描的包 --&gt;</code>

<code>    </code><code>&lt;</code><code>context:component-scan</code> <code>base-package</code><code>=</code><code>"com.abc.model"</code> <code>/&gt;</code>

<code>    </code><code>&lt;</code><code>context:component-scan</code> <code>base-package</code><code>=</code><code>"com.abc.service"</code> <code>/&gt;</code>

<code>&lt;/</code><code>bean</code><code>&gt;</code>

    上面的配置檔案中的 comtext:component-scan指定了spring将把com.abc.model包和com.abc.service包作為掃描目錄,搜尋其中帶了@component、@controller、@repository和@service等注解的類作為容器中的bean(某些是特殊的bean)。

    在基于xml配置方式下,每個bean執行個體的名稱都由其id屬性指定,而在使用注解配置spring的方式下,spring将采用約定的方式來為這些bean執行個體指定名稱,這些bean執行個體的名稱預設是bean類的首字母小寫,其他部分不變。 當然,spring也允許使用@component注解時自定義bean的名字,如上面的people的注解@component("pp"),意為把people的bean執行個體命名為“pp”。

    當使用xml配置bean時,可以通過scope來指定bean的作用域,在使用注解時,可通過@scope注解來标注,隻要在該注解中提供作用域的名稱即可。例如上面的:

    另外,我們還可以通過為&lt;component-scan&gt;元素添加&lt;include-filter&gt;和&lt;exclude-filter&gt;子元素來限制spring bean的類。滿足&lt;include-filter&gt;定義的規則的java類,才會被當作bean處理,滿足&lt;exclude-filter&gt;規則的java類,則不會當作bean處理。使用這兩個屬性時,都需要為其指定下面兩個元素:

type:指定過濾器類型

expression:知道你過過濾器所需要的表達式

    spring支援以下幾種過濾器:

annotation:annotation過濾器,該過濾器需要指定一個annotation名

assignable:類名過濾器,該過濾器直接指定一個java類

regex:正規表達式過濾器,該過濾器指定一個正規表達式,比對該正規表達式的java類将滿足該過濾規則。如:com\.abc\.*

aspectj:aspectj過濾器

<code>&lt;!-- 指定spring将要掃描的包 --&gt;</code>

<code>&lt;</code><code>context:component-scan</code> <code>base-package</code><code>=</code><code>"com.abc.model"</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>context:include-filter</code> <code>type</code><code>=</code><code>"regex"</code> <code>expression</code><code>=</code><code>".*peo*"</code> <code>/&gt;</code>

<code>    </code><code>&lt;</code><code>context:exclude-filter</code> <code>type</code><code>=</code><code>"regex"</code> <code>expression</code><code>=</code><code>".*ppp*"</code> <code>/&gt;</code>

<code>&lt;/</code><code>context:component-scan</code><code>&gt;</code>

二、使用@resource配置依賴

    @resource注解位于java.annotation包下,是來自javaee規範中的一個注解,spring直接借鑒了該注解,其作用是為目标bean指定協作者bean。

    @resource有一個name屬性,在預設情況下,spring将這個值解釋為需要被注入的bean執行個體的名字。換句話說:使用@resource與配置檔案中&lt;property&gt;中的ref屬性有相同的效果。例如:

<code>@component</code>

<code>    </code><code>private</code> <code>work work;</code>

<code>    </code><code>@resource</code><code>(name=</code><code>"computerwork"</code><code>)</code>

<code>    </code><code>public</code> <code>void</code> <code>setwork(work work) {</code>

<code>        </code><code>this</code><code>.work = work;</code>

    上例就是将“computerwork”注入該setwork方法,也就是将容器中的computerwork作為setwork()方法的參數傳入。

    @resource方法不僅可以修飾setter方法,還可以直接修飾field。如果使用@resource修飾field将更加簡單,此時spring将直接使用javaee中規範的field注入,此時連setter方法都可以不要,例如可以将剛剛的例子改寫為:

<code>    </code><code>//do not need setter anymore</code>

    使用@resource注解時,其name屬性也可以省略,預設情況下,name屬性是該setter方法去掉set子串後,再将首字母小寫得到的值。例如:使用@resource标注setwork方法,則spring預設會注入容器中名為work的元件。

    當使用@resource标注一個field時,如果省略name屬性,則name屬性預設與被标注的field同名。例如:使用@resource标注private work work域,則spring将把容器中名為work的元件注入。

三、使用@postconstruct和@predestroy定制bean的生命周期行為

<code>    </code><code>@postconstruct</code>

<code>    </code><code>public</code> <code>void</code> <code>init() {</code>

<code>        </code><code>//...other operations here</code>

<code>        </code><code>system.out.println(</code><code>"init方法:所有依賴注入完成"</code><code>);</code>

<code>    </code><code>@predestroy</code>

<code>    </code><code>public</code> <code>void</code> <code>destroy() {</code>

<code>        </code><code>system.out.println(</code><code>"destroy方法:銷毀之前"</code><code>);</code>

四、spring3.0新增的注解

    @dependson可以修飾bean類或方法,使用該注解時可以指定一個字元串數組作為參數,每個數組元素對應一個強制初始化的bean,例如:

<code>@dependson</code><code>({</code><code>"computerwork"</code><code>,</code><code>"job"</code><code>})</code>

<code>    </code><code>@resource</code><code>(name=</code><code>"job"</code><code>)</code>

<code>    </code><code>private</code> <code>job job;</code>

    上面的代碼使用了@dependson修飾了people類,這就指定在初始化people bean之前,會強制初始化computerwork和job兩個bean。

    @lazy注解主要用于修飾spring bean類,用于指定該bean的預初始化行為,使用該注解時可以指定一個bool類型的值,該屬性決定是否預初始化這個bean。如果該值為true,則表示該bean不會預初始化。例如:

<code>@dependson</code><code>(</code><code>true</code><code>) </code><code>//這個bean不會預初始化</code>

<code>    </code><code>//....</code>

五、自動裝配與精确裝配

    spring提供了@autowired注解來指定自動裝配,使用@autowired可以标注setter方法,普通方法,field和構造器等。例如:

    上面的代碼使用了@autowired指定對setwork()方法進行自動裝配,spring會将自動搜尋器中類型為work的bean執行個體,并将該bean執行個體作為setwork()方法的參數傳入,注入給people執行個體。由此可見,當使用@autowired注解标注setter方法時,預設使用的是bytype的自動裝配政策。

    spring允許使用@autowired來标注同時注入多個參數的普通方法,例如:

<code>    </code><code>public</code> <code>void</code> <code>init(work work, job job) {</code>

<code>        </code><code>this</code><code>.job = job;</code>

    使用@autowired來标注field和構造器的例子:

<code>    </code><code>private</code> <code>school school;</code>

<code>    </code><code>public</code> <code>people(work work, job job) {</code>

    當使用@autowired來标注一個field時,spring将會把容器中與該field類型比對的bean注入該屬性。例如程式中使用@autowired标注了school屬性,則spring會自動搜尋容器中的school執行個體,并将該執行個體設定成該school field的值;如果此時容器中不止一個school類型的bean,則spring将抛出一個beancreateexception異常。

    @autowired甚至可以用來修飾數組:

<code>    </code><code>private</code> <code>school[] schools;</code>

    在這種情況下,spring将會搜集容器中所有類型為school的bean,并用這些bean建立一個數組,最後将這個數組注入給people的schools屬性。與此類似的是,@autowired也可以标注集合field,或标注形參類型是集合的方法,spring對這種集合屬性,集合形參的處理與前面數組的處理是完全相同的。例如:

<code>    </code><code>private</code> <code>set&lt;school&gt; schools;</code>

<code>    </code><code>private</code> <code>set&lt;work&gt; works;</code>

<code>    </code><code>public</code> <code>void</code> <code>setworks(set&lt;work&gt; works) {</code>

<code>        </code><code>this</code><code>.works = works;</code>

    對于這種集合類型的參數而言,程式代碼中必須使用泛型,正如上面的程式所示,程式制定了該方法參數是set&lt;work&gt;類型,這表明,spring會自動搜尋容器中所有的work類型執行個體,并将這些示例注入到到works屬性中。如果程式中沒有使用泛型來指明集合元素類型, 則spring将不知所措。

    正如上面看到的,@autowired總是采用bytype的自動裝配政策,在這種政策下,符合自動裝配的類型的候選bean常常有多個,這個時候就可能引起異常了(對于數組,集合類型的參數則不會)。

    為了實作精确的自動裝配,spring提供了@qualifier注解,通過使用這個注解,允許bean辨別來指定自動裝配。通常會為@qualifier指定一個名字,表示精确定位id為這個名字的bean,@qualifier通常用于修飾field,例如:

<code>    </code><code>@qualifier</code><code>(</code><code>"juniorhighschool"</code><code>)</code>

<code>    </code><code>//setter</code>

<code>    </code><code>public</code> <code>void</code> <code>setschool(school school) {</code>

<code>        </code><code>this</code><code>.shool = school;</code>

    上面的配置檔案中指定了school将使用自動裝配,且精确指定了被裝配的bean執行個體名稱為juniorhighschool,這意味着如果spring容器中有多個school類型的bean,隻會将一個名為juniorhighschool的bean注入進來。

    除此之外,spring還允許使用@qualifier來标注方法的形參,例如:

<code>public</code> <code>class</code> <code>people {   </code>

<code>    </code><code>public</code> <code>void</code> <code>setschool(</code><code>@qualifier</code><code>(</code><code>"juniorhighschool"</code><code>)school school) {</code>