天天看点

浅析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>