详解Java的Spring框架中的注解的用法
1. 使用spring注解来注入属性
1.1. 使用注解以前我们是怎样注入属性的
类的实现:
class usermanagerimpl implements usermanager { private userdao userdao; public void setuserdao(userdao userdao) { this.userdao = userdao; } ... }
配置文件:
<bean id="usermanagerimpl" class="com.kedacom.spring.annotation.service.usermanagerimpl"> <property name="userdao" ref="userdao" /> </bean> <bean id="userdao" class="com.kedacom.spring.annotation.persistence.userdaoimpl"> <property name="sessionfactory" ref="mysessionfactory" /> </bean>
1.2. 引入@autowired注解(不推荐使用,建议使用@resource)
类的实现(对成员变量进行标注)
public class usermanagerimpl implements usermanager { @autowired private userdao userdao; ... }
或者(对方法进行标注)
usermanagerimpl implements usermanager { private userdao userdao; @autowired public void setuserdao(userdao userdao) { this.userdao = userdao; } ... }
配置文件
<bean id="usermanagerimpl" class="com.kedacom.spring.annotation.service.usermanagerimpl" /> <bean id="userdao" class="com.kedacom.spring.annotation.persistence.userdaoimpl"> <property name="sessionfactory" ref="mysessionfactory" /> </bean>
@autowired可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作。以上两种不同实现方式中,@autowired的标注位置不同,它们都会在spring在初始化usermanagerimpl这个bean时,自动装配userdao这个属性,区别是:第一种实现中,spring会直接将userdao类型的唯一一个bean赋值给userdao这个成员变量;第二种实现中,spring会调用setuserdao方法来将userdao类型的唯一一个bean装配到userdao这个属性。
1.3. 让@autowired工作起来
要使@autowired能够工作,还需要在配置文件中加入以下代码
class="org.springframework.beans.factory.annotation.autowiredannotationbeanpostprocessor" />
1.4. @qualifier
@autowired是根据类型进行自动装配的。在上面的例子中,如果当spring上下文中存在不止一个userdao类型的bean时,就会抛出beancreationexception异常;如果spring上下文中不存在userdao类型的bean,也会抛出beancreationexception异常。我们可以使用@qualifier配合@autowired来解决这些问题。
1. 可能存在多个userdao实例
@autowired public void setuserdao(@qualifier("userdao") userdao userdao) { this.userdao = userdao; }
这样,spring会找到id为userdao的bean进行装配。
2. 可能不存在userdao实例
@autowired(required = false) public void setuserdao(userdao userdao) { this.userdao = userdao; }
1.5. @resource(jsr-250标准注解,推荐使用它来代替spring专有的@autowired注解)
spring 不但支持自己定义的@autowired注解,还支持几个由jsr-250规范定义的注解,它们分别是@resource、@postconstruct以及@predestroy。
@resource的作用相当于@autowired,只不过@autowired按bytype自动注入,而@resource默认按byname自动注入罢了。@resource有两个属性是比较重要的,分别是name和type,spring将@resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byname的自动注入策略,而使用type属性时则使用bytype自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byname自动注入策略。
@resource装配顺序
如果同时指定了name和type,则从spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
如果既没有指定name,又没有指定type,则自动按照byname方式进行装配(见2);如果没有匹配,则回退为一个原始类型(userdao)进行匹配,如果匹配则自动装配;
1.6. @postconstruct(jsr-250)
在方法上加上注解@postconstruct,这个方法就会在bean初始化之后被spring容器执行(注:bean初始化包括,实例化bean,并装配bean的属性(依赖注入))。
它的一个典型的应用场景是,当你需要往bean里注入一个其父类中定义的属性,而你又无法复写父类的属性或属性的setter方法时,如:
public class userdaoimpl extends hibernatedaosupport implements userdao { private sessionfactory mysessionfacotry; @resource public void setmysessionfacotry(sessionfactory sessionfacotry) { this.mysessionfacotry = sessionfacotry; } @postconstruct public void injectsessionfactory() { super.setsessionfactory(mysessionfacotry); } ... }
这里通过@postconstruct,为userdaoimpl的父类里定义的一个sessionfactory私有属性,注入了我们自己定义的sessionfactory(父类的setsessionfactory方法为final,不可复写),之后我们就可以通过调用super.getsessionfactory()来访问该属性了。
1.7. @predestroy(jsr-250)
在方法上加上注解@predestroy,这个方法就会在bean初始化之后被spring容器执行。由于我们当前还没有需要用到它的场景,这里不不去演示。其用法同@postconstruct。
1.8. 使用<context:annotation-config />简化配置
spring2.1添加了一个新的context的schema命名空间,该命名空间对注释驱动、属性文件引入、加载期织入等功能提供了便捷的配置。我们知道注释本身是不会做任何事情的,它仅提供元数据信息。要使元数据信息真正起作用,必须让负责处理这些元数据的处理器工作起来。
autowiredannotationbeanpostprocessor和commonannotationbeanpostprocessor就是处理这些注释元数据的处理器。但是直接在spring配置文件中定义这些bean显得比较笨拙。spring为我们提供了一种方便的注册这些beanpostprocessor的方式,这就是<context:annotation-config />:
<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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config /> </beans>
<context:annotationconfig />将隐式地向spring容器注册autowiredannotationbeanpostprocessor、commonannotationbeanpostprocessor、 persistenceannotationbeanpostprocessor以及requiredannotationbeanpostprocessor这4个beanpostprocessor。
2. 使用spring注解完成bean的定义
以上我们介绍了通过@autowired或@resource来实现在bean中自动注入的功能,下面我们将介绍如何注解bean,从而从xml配置文件中完全移除bean定义的配置。
2.1. @component(不推荐使用)、@repository、@service、@controller
只需要在对应的类上加上一个@component注解,就将该类定义为一个bean了:
@component public class userdaoimpl extends hibernatedaosupport implements userdao { ... }
使用@component注解定义的bean,默认的名称(id)是小写开头的非限定类名。如这里定义的bean名称就是userdaoimpl。你也可以指定bean的名称:
@component("userdao")
@component是所有受spring管理组件的通用形式,spring还提供了更加细化的注解形式:@repository、@service、@controller,它们分别对应存储层bean,业务层bean,和展示层bean。目前版本(2.5)中,这些注解与@component的语义是一样的,完全通用,在spring以后的版本中可能会给它们追加更多的语义。所以,我们推荐使用@repository、@service、@controller来替代@component。
2.2. 使用<context:component-scan />让bean定义注解工作起来
<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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:component-scan base-package="com.kedacom.ksoa" /> </beans>
这里,所有通过<bean>元素定义bean的配置内容已经被移除,仅需要添加一行<context:component-scan />配置就解决所有问题了——spring xml配置文件得到了极致的简化(当然配置元数据还是需要的,只不过以注释形式存在罢了)。<context:component-scan />的base-package属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。
<context:component-scan />还允许定义过滤器将基包下的某些类纳入或排除。spring支持以下4种类型的过滤方式:
过滤器类型 表达式范例 说明
注解 org.example.someannotation 将所有使用someannotation注解的类过滤出来
类名指定 org.example.someclass 过滤指定的类
正则表达式 com\.kedacom\.spring\.annotation\.web\..* 通过正则表达式过滤一些类
aspectj表达式 org.example..*service+ 通过aspectj表达式过滤一些类
以正则表达式为例,我列举一个应用实例:
<context:component-scan base-package="com.casheen.spring.annotation"> <context:exclude-filter type="regex" expression="com\.casheen\.spring\.annotation\.web\..*" /> </context:component-scan>
值得注意的是<context:component-scan />配置项不但启用了对类包进行扫描以实施注释驱动bean定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了autowiredannotationbeanpostprocessor和commonannotationbeanpostprocessor),因此当使用<context:component-scan />后,就可以将<context:annotation-config />移除了。
2.3. 使用@scope来定义bean的作用范围
在使用xml定义bean时,我们可能还需要通过bean的scope属性来定义一个bean的作用范围,我们同样可以通过@scope注解来完成这项工作:
@scope("session") @component() public class usersessionbean implements serializable { ... }
3.在使用annotation之前定义三个bean之间的关系是这样的
package com.baobaotao; public class office { private string officeno =”001”; //省略 get/setter @override public string tostring() { return "officeno:" + officeno; } } ackage com.baobaotao; public class car { private string brand; private double price; // 省略 get/setter @override public string tostring() { return "brand:" + brand + "," + "price:" + price; } } package com.baobaotao; public class boss { private car car; private office office; // 省略 get/setter @override public string tostring() { return "car:" + car + "\n" + "office:" + office; } }
配置文件如下:
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="boss" class="com.baobaotao.boss"> <property name="car" ref="car"/> <property name="office" ref="office" /> </bean> <bean id="office" class="com.baobaotao.office"> <property name="officeno" value="002"/> </bean> <bean id="car" class="com.baobaotao.car" scope="singleton"> <property name="brand" value=" 红旗 ca72"/> <property name="price" value="2000"/> </bean> </beans>
测试文件如下:
import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; public class annoioctest { public static void main(string[] args) { string[] locations = {"beans.xml"}; applicationcontext ctx = new classpathxmlapplicationcontext(locations); boss boss = (boss) ctx.getbean("boss"); system.out.println(boss); } }
4.接下来我们可以使用autowired来注解,他可以对成员变量,方法及构造函数进行标准,完成自动装配的工作。
autoware来注解成员变量的用法
package com.baobaotao; import org.springframework.beans.factory.annotation.autowired; public class boss { @autowired private car car; @autowired private office office; … }
相应的配置文件如下:
<?xml version="1.0" encoding="utf-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <!-- 该 beanpostprocessor 将自动起作用,对标注 @autowired 的 bean 进行自动注入 --> <bean class="org.springframework.beans.factory.annotation. autowiredannotationbeanpostprocessor"/> <!-- 移除 boss bean 的属性注入配置的信息 --> <bean id="boss" class="com.baobaotao.boss"/> <bean id="office" class="com.baobaotao.office"> <property name="officeno" value="001"/> </bean> <bean id="car" class="com.baobaotao.car" scope="singleton"> <property name="brand" value=" 红旗 ca72"/> <property name="price" value="2000"/> </bean> </beans>
autoware也可以用在setter方法及构造函数上
package com.baobaotao; public class boss { private car car; private office office; @autowired public void setcar(car car) { this.car = car; } @autowired public void setoffice(office office) { this.office = office; } … } package com.baobaotao; public class boss { private car car; private office office; @autowired public boss(car car ,office office){ this.car = car; this.office = office ; } … }
当候选bean的数目为0时,我们可以使用@autowired(required = false)来防止spring找不到bean时报错。
当有多个候选bean的时候,我们可以通过@qualifier 注释指定注入 bean 的名称。
@autowired 和 @qualifier 结合使用时,自动注入的策略就从 bytype 转变成 byname 了。@autowired 可以对成员变量、方法以及构造函数进行注释,而 @qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 spring 不将 @autowired 和 @qualifier 统一成一个注释类。
5.@resorce是按照名字来进行反射,他有两个参数,name和type,使用name即按照byname来映射,使用type即按照bytype来进行映射。
package com.baobaotao; import javax.annotation.resource; public class boss { // 自动注入类型为 car 的 bean @resource private car car; // 自动注入 bean 名称为 office 的 bean @resource(name = "office") private office office; } @postconstructor和predestory是用来注解类初始化后和销毁前的方法。 package com.baobaotao; import javax.annotation.resource; import javax.annotation.postconstruct; import javax.annotation.predestroy; public class boss { @resource private car car; @resource(name = "office") private office office; @postconstruct public void postconstruct1(){ system.out.println("postconstruct1"); } @predestroy public void predestroy1(){ system.out.println("predestroy1"); } … }
6.@compent可以直接定义bean,这样xml配置文件中就不需要配置bean了
package com.baobaotao; import org.springframework.stereotype.component; @component public class car { … } package com.baobaotao; import org.springframework.stereotype.component; @component public class office { private string officeno = "001"; … } package com.baobaotao; import org.springframework.beans.factory.annotation.autowired; import org.springframework.beans.factory.annotation.required; import org.springframework.beans.factory.annotation.qualifier; import org.springframework.stereotype.component; @component("boss") public class boss { @autowired private car car; @autowired private office office; … }
@component 有一个可选的入参,用于指定 bean 的名称,在 boss 中,我们就将 bean 名称定义为“boss”。一般情况下,bean 都是 singleton 的,需要注入 bean 的地方仅需要通过 bytype 策略就可以自动注入了,所以大可不必指定 bean 的名称。
<?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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:component-scan base-package="com.baobaotao"/> </beans>
7.@scope可以用来指定其目标
package com.baobaotao; import org.springframework.context.annotation.scope; … @scope("prototype") @component("boss") public class boss {
spring 2.5 中除了提供 @component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@repository、@service 和 @controller。在目前的 spring 版本中,这 3 个注释和 @component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(web 层)相对应。虽然目前这 3 个注释和 @component 相比没有什么新意,但 spring 将在以后的版本中为它们添加特殊的功能。所以,如果 web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @repository、@service 和 @controller 对分层中的类进行注释,而用 @component 对那些比较中立的类进行注释。