零配置 之 12.2 注解实现Bean依赖注入 ——跟我学spring3
12.2 注解实现Bean依赖注入
12.2.1 概述
@H_
301_4@ 注解实现Bean配置主要用来进行如依赖注入、生命周期回调
方法定义等,不能消除XML
文件中的Bean元数据定义,
且基于XML配置中的依赖注入的数据将覆盖基于注解配置中的依赖注入的数据。
@H_
301_4@
@H_
301_4@ Spring3的基于注解实现Bean依赖注入
支持如下三种注解:
- Spring自带依赖注入注解:Spring自带的一套依赖注入注解;
- JSR-250注解:Java平台的公共注解,是Java EE 5规范之一,在JDK6中默认包含这些注解,从Spring2.5开始支持。
- JSR-330注解:Java 依赖注入标准,Java EE 6规范之一,可能在加入到未来JDK版本,从Spring3开始支持;
- JPA注解:用于注入持久化上下文和尸体管理器。
@H_
301_4@ 这三种类型的注解在Spring3中都
支持,类似于注解事务
支持,想要使用这些注解需要在Spring容器中开启注解驱动
支持,即使用如下配置方式开启:
@H_
301_4@
java代码:
- <beansxmlns="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:
- http:
- http:
- //www.springframework.org/schema/context/spring-context-3.0.xsd">
-
- <context:annotation-config/>
- </beans>
-
@H_
301_4@ 这样就能使用注解驱动依赖注入了,该
配置文件位于“resources/ chapter12/dependecyInjectWithAnnotation.xml”。
12.2.2 Spring自带依赖注入注解
@H_
301_4@
一、@required:依赖检查;
@H_
301_4@ 对应于基于XML配置中的依赖检查,但XML配置的依赖检查将检查所有setter
方法,详见【3.3.4 依赖检查】;
@H_
301_4@ 基于@
required的依赖检查表示注解的setter
方法必须,即必须通过在XML配置中配置setter注入,如果没有配置在容器启动时会抛出异常从而保证在运行时不会遇到空指针异常,@
required只能放置在setter
方法上,且通过XML配置的setter注入,可以使用如下方式来指定:
@Requried
setter方法
@H_
301_4@ 1、准备测试Bean
packagecn.javass.spring.chapter12;
publicclassTestBean{
privateStringmessage;
@required
voidsetMessage(Stringmessage){
this.message=message;
}
publicStringgetMessage(){
returnmessage;
}
}
@H_
301_4@ 2、在Spring
配置文件(chapter12/dependecyInjectWithAnnotation.xml)
添加如下Bean配置:
<beanid="testBean"class="cn.javass.spring.chapter12.TestBean">
<propertyname="message"ref="message"/>
</bean>
<beanid="message"class="java.lang.String">
<constructor-argindex="0"value="hello"/>
</bean>
@H_
301_4@ 3、测试类和测试
方法如下:
classDependencyInjectWithAnnotationTest{
privatestaticStringconfigLocation="classpath:chapter12/dependecyInjectWithAnnotation.xml";
staticApplicationContextctx=newClassPathXmlApplicationContext("configLocation");
//1、Spring自带依赖注入注解
@Test
voidtestrequiredForXmlSetterInject(){
TestBeantestBean=ctx.getBean("testBean",TestBean.class);
Assert.assertEquals("hello",testBean.getMessage());
}
@H_301_4@ 在XML配置文件中必须指定setter注入,否则在Spring容器启动时将抛出如下异常:
org.springframework.beans.factory.BeanCreationException:
Errorcreatingbeanwithname'testBean'definedinclasspathresource[chapter12/dependecyInjectWithAnnotation.xml]:InitializationofbeanFailed;
nestedexceptionisorg.springframework.beans.factory.BeanInitializationException:Property'message'isrequiredforbean'testBean'
@H_301_4@ 二、@Autowired:自动装配
@H_301_4@ 自动装配,用于替代基于XML配置的自动装配,详见【3.3.3 自动装配】。
@H_301_4@ 基于@Autowired的自动装配,默认是根据类型注入,可以用于构造器、字段、方法注入,使用方式如下:
@Autowired(required=true)
构造器、字段、方法
@H_301_4@ @Autowired默认是根据参数类型进行自动装配,且必须有一个Bean候选者注入,如果允许出现0个Bean候选者需要设置属性“required=false”,“required”属性含义和@required一样,只是@required只适用于基于XML配置的setter注入方式。
@H_301_4@ (1)、构造器注入:通过将@Autowired注解放在构造器上来完成构造器注入,默认构造器参数通过类型自动装配,如下所示:
@H_301_4@ 1、准备测试Bean,在构造器上添加@AutoWired注解:
importorg.springframework.beans.factory.annotation.Autowired;
classTestBean11{
privateStringmessage;
@Autowired
privateTestBean11(Stringmessage){
this.message=message;
<beanid="testBean11"class="cn.javass.spring.chapter12.TestBean11"/>
@H_301_4@ 3、测试类如下:
voidtestAutowiredForConstructor(){
TestBean11testBean11=ctx.getBean("testBean11",TestBean11. 在Spring配置文件中没有对“testBean11”进行构造器注入和setter注入配置,而是通过在构造器上添加@ Autowired来完成根据参数类型完成构造器注入。
@H_301_4@ (2)、字段注入:通过将@Autowired注解放在构造器上来完成字段注入。
@H_301_4@ 1、准备测试Bean,在字段上添加@AutoWired注解:
class
TestBean12{
//字段注入
//省略getter和setter
<beanid="testBean12"class="cn.javass.spring.chapter12.TestBean12"/>
@H_301_4@ 3、测试方法如下:
voidtestAutowiredForField(){
TestBean12testBean12=ctx.getBean("testBean12",TestBean12. 字段注入在基于XML配置中无相应概念,字段注入不支持静态类型字段的注入。
@H_301_4@ (3)、方法参数注入:通过将@Autowired注解放在方法上来完成方法参数注入。
@H_301_4@ 1、准备测试Bean,在方法上添加@AutoWired注解:
class
TestBean13{
//setter方法注入
voidsetMessage(Stringmessage){
publicStringgetMessage(){
returnmessage;
classTestBean14{
privateList<String>list;
true)
voidinitMessage(Stringmessage,ArrayList<String>list){
this.list=list;
//省略getter和setter
<beanid="testBean13"class="cn.javass.spring.chapter12.TestBean13"/>
<beanid="testBean14"class="cn.javass.spring.chapter12.TestBean14"/>
<beanid="list"class="java.util.ArrayList">
<constructor-argindex="0">
<list>
<refbean="message"/>
<refbean="message"/>
</list>
</constructor-arg>
voidtestAutowiredForMethod(){
TestBean13testBean13=ctx.getBean("testBean13",TestBean13.
TestBean14testBean14=ctx.getBean("testBean14",TestBean14.class);
Assert.assertEquals("hello",testBean14.getMessage());
Assert.assertEquals(ctx.getBean("list",List.class),testBean14.getList());
@H_301_4@ 方法参数注入除了支持setter方法注入,还支持1个或多个参数的普通方法注入,在基于XML配置中不支持1个或多个参数的普通方法注入,方法注入不支持静态类型方法的注入。
@H_301_4@ 注意“initMessage(String message,ArrayList<String> list)”方法签名中为什么使用ArrayList而不是List呢?具体参考【3.3.3 自动装配】一节中的集合类型注入区别。
@H_301_4@ 三、@Value:注入SpEL表达式;
@H_301_4@ 用于注入SpEL表达式,可以放置在字段方法或参数上,使用方式如下:
@Value(value="SpEL表达式")
字段、方法、参数
@H_301_4@ 1、可以在类字段上使用该注解:
@Value(value="#{message}")
privateStringmessage;
@H_301_4@ 2、可以放置在带@Autowired注解的方法的参数上:
@Autowired
voidinitMessage(@Value(value="#{message}#{message}")Stringmessage){
3、还可以放置在带@Autowired注解的构造器的参数上:
privateTestBean43( 具体测试详见DependencyInjectWithAnnotationTest 类的testValueInject测试方法。
@H_301_4@ 四、@Qualifier:限定描述符,用于细粒度选择候选者;
@H_301_4@ @Autowired默认是根据类型进行注入的,因此如果有多个类型一样的Bean候选者,则需要限定其中一个候选者,否则将抛出异常,详见【3.3.3 自动装配】中的根据类型进行注入;
@H_301_4@ @Qualifier限定描述符除了能根据名字进行注入,但能进行更细粒度的控制如何选择候选者,具体使用方式如下:
@Qualifier(value="限定标识符")
(1)、根据基于XML配置中的<qualifier>标签指定的名字进行注入,使用如下方式指定名称:
<qualifiertype="org.springframework.beans.factory.annotation.Qualifier"value="限定标识符"/>
@H_301_4@ 其中type属性可选,指定类型,默认就是Qualifier注解类,name就是给Bean候选者指定限定标识符,一个Bean定义中只允许指定类型不同的<qualifier>,如果有多个相同type后面指定的将覆盖前面的。
@H_301_4@ 1、准备测试Bean:
importjavax.sql.DataSource;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.beans.factory.annotation.Qualifier;
classTestBean31{
privateDataSourcedataSource;
@Autowired
//根据<qualifier>标签指定Bean限定标识符
voidinitDataSource(@Qualifier("MysqLDataSource")DataSourcedataSource){
this.dataSource=dataSource;
publicDataSourcegetDataSource(){
returndataSource;
<beanid="testBean31"class="cn.javass.spring.chapter12.TestBean31"/>
@H_301_4@ 我们使用@Qualifier("MysqLDataSource")来指定候选Bean的限定标识符,我们需要在配置文件中使用<qualifier>标签来指定候选Bean的限定标识符“MysqLDataSource”:
<beanid="MysqLDataSourceBean"class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<qualifiervalue="MysqLDataSource"/>
</bean>
voidtestQualifierInject1(){
TestBean31testBean31=ctx.getBean("testBean31",TestBean31.try{
//使用<qualifier>指定的标识符只能被@Qualifier使用
ctx.getBean("MysqLDataSource");
Assert.fail();
}catch(Exceptione){
//找不到该Bean
Assert.assertTrue(einstanceofNoSuchBeanDefinitionException);
Assert.assertEquals(ctx.getBean("MysqLDataSourceBean"),testBean31.getDataSource());
从测试可以看出使用<qualifier>标签指定的限定标识符只能被@Qualifier使用,不能作为Bean的标识符,如“ctx.getBean("MysqLDataSource")”是获取不到Bean的。
@H_301_4@ (2)、缺省的根据Bean名字注入:最基本方式,是在Bean上没有指定<qualifier>标签时一种容错机制,即缺省情况下使用Bean标识符注入,但如果你指定了<qualifier>标签将不会发生容错。
classTestBean32{
privateDataSourcedataSource;
@Qualifier(value="MysqLDataSource2")
//@Qualifier(value="MysqLDataSourceBean")
//是错误的注入,不会发生回退容错,因为你指定了<qualifier>
voidinitDataSource(DataSourcedataSource){
this.dataSource=dataSource;
publicDataSourcegetDataSource(){
returndataSource;
<beanid="testBean32"class="cn.javass.spring.chapter12.TestBean32"/>
<beanid="oracleDataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"/>
voidtestQualifierInject2(){
TestBean32testBean32=ctx.getBean("testBean32",TestBean32. Assert.assertEquals(ctx.getBean("oracleDataSource"),testBean32.getDataSource());
默认情况下(没指定<qualifier>标签)@Qualifier的value属性将匹配Bean 标识符。
@H_301_4@ (3)、扩展@Qualifier限定描述符注解:对@Qualifier的扩展来提供细粒度选择候选者;
@H_301_4@ 具体使用方式就是自定义一个注解并使用@Qualifier注解其即可使用。
@H_301_4@ 首先让我们考虑这样一个问题,如果我们有两个数据源,分别为MysqL和Oracle,因此注入两者相关资源时就牵扯到数据库相关,如在DAO层注入SessionFactory时,当然可以采用前边介绍的方式,但为了简单和直观我们希望采用自定义注解方式。
@H_301_4@ 1、扩展@Qualifier限定描述符注解来分别表示MysqL和Oracle数据源
packagecn.javass.spring.chapter12.qualifier;
/**表示注入MysqL相关*/
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@interfaceMysqL{
/**表示注入Oracle相关*/
@interfaceOracle{
@H_301_4@ 2、准备测试Bean:
classTestBean33{
privateDataSourceMysqLDataSource;
privateDataSourceoracleDataSource;
@MysqLDataSourceMysqLDataSource,@OracleDataSourceoracleDataSource){
this.MysqLDataSource=MysqLDataSource;
this.oracleDataSource=oracleDataSource;
publicDataSourcegetMysqLDataSource(){
returnMysqLDataSource;
publicDataSourcegetOracleDataSource(){
returnoracleDataSource;
3、在Spring配置文件(chapter12/dependecyInjectWithAnnotation.xml)添加如下Bean配置:
<beanid="testBean33"class="cn.javass.spring.chapter12.TestBean33"/>
@H_301_4@ 4、在Spring修改定义的两个数据源:
<qualifiertype="cn.javass.spring.chapter12.qualifier.MysqL"/>
</bean>
<beanid="oracleDataSource" <qualifiertype="cn.javass.spring.chapter12.qualifier.Oracle"/>
5、测试方法如下:
voidtestQualifierInject3(){
TestBean33testBean33=ctx.getBean("testBean33",TestBean33.MysqLDataSoruce());
Assert.assertEquals(ctx.getBean("oracleDataSource"),testBean33.getOracleDataSoruce());
测试也通过了,说明我们扩展的@Qualifier限定描述符注解也能很好工作。
@H_301_4@ 前边演示了不带属性的注解,接下来演示一下带参数的注解:
@H_301_4@ 1、首先定义数据库类型:
enumDataBase{
ORACLE,MysqL;
2、其次扩展@Qualifier限定描述符注解
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
@interfaceDataSourceType{
Stringip();
DataBasedatabase();
3、准备测试Bean:
importcn.javass.spring.chapter12.qualifier.DataBase;
importcn.javass.spring.chapter12.qualifier.DataSourceType;
classTestBean34{
privateDataSourceMysqLDataSource;
privateDataSourceoracleDataSource;
voidinitDataSource(
@DataSourceType(ip="localhost",database=DataBase.MysqL)
DataSourceMysqLDataSource,
DataSourceoracleDataSource){
this.MysqLDataSource=MysqLDataSource;
this.oracleDataSource=oracleDataSource;
//省略getter方法
4、在Spring配置文件(chapter12/dependecyInjectWithAnnotation.xml)添加如下Bean配置:
<beanid="testBean34"class="cn.javass.spring.chapter12.TestBean34"/>
@H_301_4@ 5、在Spring修改定义的两个数据源:
<qualifiertype="cn.javass.spring.chapter12.qualifier.DataSourceType">
<attributekey="ip"value="localhost"/>
<attributekey="database"value="MysqL"/>
</qualifier>
<qualifiertype="cn.javass.spring.chapter12.qualifier.DataSourceType">
<attributekey="ip"value="localhost"/>
<attributekey="database"value="ORACLE"/>
</qualifier>
6、测试方法如下:
TestBean34testBean34=ctx.getBean("testBean34",TestBean34.MysqLDataSource());
测试也通过了,说明我们扩展的@Qualifier限定描述符注解也能很好工作。
@H_301_4@ 四、自定义注解限定描述符:完全不使用@Qualifier,而是自己定义一个独立的限定注解;
@H_301_4@ 1、首先使用如下方式定义一个自定义注解限定描述符:
@interfaceCustomQualifier{
Stringvalue();
classTestBean35{
privateDataSourcedataSoruce;
publicTestBean35(@CustomQualifier("oracleDataSource")DataSourcedataSource){
this.dataSoruce=dataSource;
publicDataSourcegetDataSoruce(){
returndataSoruce;
<beanid="testBean35"class="cn.javass.spring.chapter12.TestBean35"/>
@H_301_4@ 4、然后在Spring配置文件中注册CustomQualifier自定义注解限定描述符,只有注册了Spring才能识别:
<beanid="customAutowireConfigurer"class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<propertyname="customQualifierTypes">
<set>
<value>cn.javass.spring.chapter12.qualifier.CustomQualifier</value>
</set>
</property>
voidtestQualifierInject5(){
TestBean35testBean35=ctx.getBean("testBean35",TestBean35. 从测试中可看出,自定义的和Spring自带的没什么区别,因此如果没有足够的理由请使用Spring自带的Qualifier注解。
@H_301_4@ 到此限定描述符介绍完毕,在此一定要注意以下几点:
- 限定标识符和Bean的描述符是不一样的;
- 多个Bean定义中可以使用相同的限定标识符;
- 对于集合、数组、字典类型的限定描述符注入,将注入多个具有相同限定标识符的Bean。
12.2.3 JSR-250注解
@H_301_4@ 一、@Resource:自动装配,默认根据类型装配,如果指定name属性将根据名字装配,可以使用如下方式来指定:
@Resource
(name="标识符")
字段或setter方法
importjavax.annotation.Resource;
classTestBean41{
@Resource(name="message")
<beanid="testBean41"class="cn.javass.spring.chapter12.TestBean41"/>
voidtestResourceInject1(){
TestBean41testBean41=ctx.getBean("testBean41",TestBean41. 使用非常简单,和@Autowired不同的是可以指定name来根据名字注入。
@H_301_4@ 使用@Resource需要注意以下几点:
- @Resource注解应该只用于setter方法注入,不能提供如@Autowired多参数方法注入;
- @Resource在没有指定name属性的情况下首先将根据setter方法对于的字段名查找资源,如果找不到再根据类型查找;
- @Resource首先将从JNDI环境中查找资源,如果没找到默认再到Spring容器中查找,因此如果JNDI环境中有和Spring容器同名的资源时需要注意。
@H_301_4@ 二、@PostConstruct和PreDestroy:通过注解指定初始化和销毁方法定义;
@H_301_4@ 1、在测试类TestBean41中添加如下代码:
@PostConstruct
voidinit(){
System.out.println("==========init");
@PreDestroy
voiddestroy(){
System.out.println("==========destroy");
2、修改测试方法如下:
voidresourceInjectTest1(){
((ClassPathXmlApplicationContext)ctx).registerShutdownHook();
TestBean41testBean41=ctx.getBean("testBean41",testBean41.getMessage());
类似于通过<bean>标签的init-method和destroy-method属性指定的初始化和销毁方法,但具有更高优先级,即注解方式的初始化和销毁方法将先执行。
12.2.4 JSR-330注解
@H_301_4@ 在测试之前需要准备JSR-330注解所需要的jar包,到spring-framework-3.0.5.RELEASE-dependencies.zip中拷贝如下jar包到类路径:
@H_301_4@
com.springsource.javax.inject-1.0.0.jar
|
@H_301_4@ 一、@Inject:等价于默认的@Autowired,只是没有required属性;
@H_301_4@ 二、@Named:指定Bean名字,对应于Spring自带@Qualifier中的缺省的根据Bean名字注入情况;
@H_301_4@ 三、@Qualifier:只对应于Spring自带@Qualifier中的扩展@Qualifier限定描述符注解,即只能扩展使用,没有value属性。
@H_301_4@ 1、首先扩展@Qualifier限定描述符注解来表示MysqL数据源
//省略部分import
importjavax.inject.Qualifier;
@Target({ElementType.FIELD,153); background-color:inherit; font-weight:bold">@interfaceJSR330MysqL{
importjavax.inject.Inject;
importjavax.inject.Named;
importcn.javass.spring.chapter12.qualifier.JSR330MysqL;
classTestBean51{
@Inject
voidinitDataSoruce(
@JSR330MysqLDataSourceMysqLDataSource,
@Named("oracleDataSource")DataSourceoracleDataSource){
//省略getter
<beanid="testBean51"class="cn.javass.spring.chapter12.TestBean51"/>
@H_301_4@ 4、在Spring修改定义的MysqLDataSourceBean数据源:
<qualifiertype="cn.javass.spring.chapter12.qualifier.JSR330MysqL"/>
voidtestInject(){
TestBean51testBean51=ctx.getBean("testBean51",TestBean51.MysqLDataSource());
测试也通过了,说明JSR-330注解也能很好工作。
@H_301_4@ 从测试中可以看出JSR-330注解和Spring自带注解依赖注入时主要有以下特点:
- Spring自带的@Autowired的缺省情况等价于JSR-330的@Inject注解;
- Spring自带的@Qualifier的缺省的根据Bean名字注入情况等价于JSR-330的@Named注解;
- Spring自带的@Qualifier的扩展@Qualifier限定描述符注解情况等价于JSR-330的@Qualifier注解。
12.2.5 JPA注解
@H_301_4@ 用于注入EntityManagerFactory和EntityManager。
classTestBean61{
@PersistenceContext(unitName="entityManagerFactory")
privateEntityManagerentityManager;
@PersistenceUnit(unitName="entityManagerFactory")
privateEntityManagerFactoryentityManagerFactory;
publicEntityManagergetEntityManager(){
returnentityManager;
publicEntityManagerFactorygetEntityManagerFactory(){
returnentityManagerFactory;
<importresource="classpath:chapter7/applicationContext-resources.xml"/>
<importresource="classpath:chapter8/applicationContext-jpa.xml"/>
<beanid="testBean61"class="cn.javass.spring.chapter12.TestBean61"/>
@H_301_4@ 此处需要引用第七章和八章的配置文件,细节内容请参考七八两章。
voidtestJpaInject(){
TestBean61testBean61=ctx.getBean("testBean61",TestBean61. Assert.assertNotNull(testBean61.getEntityManager());
Assert.assertNotNull(testBean61.getEntityManagerFactory());
测试也通过了,说明JPA注解也能很好工作。
@H_301_4@ JPA注解类似于@Resource注解同样是先根据unitName属性去JNDI环境中查找,如果没找到在到Spring容器中查找。
@H_301_4@ 原创内容,转载请注明私塾在线【http://sishuok.com/forum/blogPost/list/0/2545.html】