####7.9基于注解的容器配置 ####前言:注解配置好还是xml配置好? 二者各有千秋.
- 注解配置:更短更简洁,缺点:配置分散,不宜管理;需要重新编译.
- xml配置:不直接与代码交互,易于管理;缺点:冗长
- 使用基于Java配置,可以更好的糅合双方的优点
在字节码文件里注入组件而不是角括号申明.开发者可以将注解添加在相关的类,方法,属性声明上从来注入依赖组件.使用bean后处理器来糅合注解是spring ioc 容器扩展的通用方式.例如,spring2.0引用了强制要求的@Required注解.值得一提的是,@Autowired注解提供了和7.4.5部分中的"Autowiring collaborators"相同的作用功能,但具有更好的控制和更广的应用.@PostConstruct,@PreDestroy,@Inject,@Named
你可以在xml里明确的指出.
<?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>
已详细注册的后处理器,包括AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor;
context:annotation-config/只查找上下文中定义的注解. ####7.9.1 @Required 这个注解使用bean中属性的Setter方法;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
这个注解申明其标注的属性必须在配置时间被操作,通过一些指定值或通过自动装配.如果属性没有被操作,spring启动会有明确的失败,以避免之后的空指针异常.使用它可以保障必须引用.
####7.9.2 @Autowired @Inject注解可以替代@Autowired注解. 你可以使用@Inject注解来替代@Autowired注解;
可以在构造器中使用@Autowired注解
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
在spring4.3中,bean如果只定义了一个构造器,@Autowired注解没有使用必要.多个构造器才必须这么做.
- 将@Autorwired注解标记在set方法上
- 加在任何参数,任何名字的方法上
- 加载数组类型上.
- 加在集合类型参数上
- 可以通过Orderder接口,@Order或@Priority注解来实现Array或list里元素排序
- Map类型的参数,key是bean的名称,values包含bean的所有期待值
一般而言,如果没有可选的beans,那么注解将会失败,默认的行为是将该注解下的方法,字段,构造器视为必须的依赖,然可以如下改变它.
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
一个类只有一个构造器标志为required,但non-required的构造器可以有很多.这种情况下,为了使每一个候选者都会考虑,spring将会使用最贪婪的构造器(拥有最多的参数),这样依赖都会适配.
@Autowired注解里的Required属性,如果是false,表明如果无法注入,是可以接受的,不是必须的.如果是true,则是必须的,如果无法注入,容器无法启动
你可以用@Autowired来解决一些常用的依赖:Beanfactory,ApplicationContext,Environment,ResourceLoader,ApplicationEventPublisher,MessageSource.这些接口和他们的扩展接口,如ConfigurableApplicationContext,ResourcePatternResolver,都可以自注入.无需安装.
如@Autowired,@Inject,@Resource,@Value注解只能被spring的BeanPostProcessor实现处理,你自己的beanPostProcessor或BeanPostProcessor类型无法处理他们.这些类型必须由xml或spring的带有Bean的方法来注入.
因为自动注入可能会导致混合选项,所有有必要对其选择过程进行控制.一种方式是使用@Primary注解.@Primary表明一个特定的bean在很多同一类型的候选中,必须要选择该bean的实例注入.
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
下文中注入的是名为firstMovieCatalog的bean的实例; public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
xml配置文件如下:
<?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/>
<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
####7.9.4 Fine-tuning annotation-based autowiring with qualifiers(基于注解的候选者的自动注入的微调) @Qualifier是@Autowired控制的另一个工具.
- 最简单的例子,Qualifier可以是一个简单的值
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
这个@Qualifier注解可以指定构造器参数或方法参数;
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main")MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
根据上面的的类定义,将会匹配qualifier的值为'mian'的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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier value="action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
一般而言,bean的名字会默认是qualifier的值.因此,你可以使用'main'来替代内嵌的限定元素(它会导致相同的匹配结果).然而,尽管你可以使用本公约以名字来制定特定的bean,然@Autowired实际上是类型驱动注入包含语义限制的.这意味着限定符的值,同bean的名字回收,通常只是窄化类型匹配的集合;他们没有从语法上指向一个独一无二的bean的id.好的限制符,如'main','EMEA'或者'persistent',表达了一个特定的不同于bean的id的独立组件,毕竟bean的id会在匿名的bean定义里自动产生;
Qualifiers也适用于类型集合,例如,上文所说的Set<MovieCatalog>.在这种情况下,所有匹配的bean会依据宣布的匹配符注入为一个集合.它表明限制符不是唯一的,他们只是简单的构成过滤条件.例如,你可以定义混合的MovieCatalog的bean以相同的限制符'action'.所有的符合@Qualifier("action")条件的bean将注入到Set<MovieCatalog>集合里.
-
如果你要倾向于通过名字的注解驱动注入,不推荐@Autowired,即使通过@Qualifier值来指向一个bean是技术可行的.你应该使用@Resource注解,它在语义上通过唯一的名字来定义目标的组件,而他们的申明类型并不会参与匹配.@Atuowired则是另外的语义:通过类型选择候选者之后,才会考虑特定的string的qualifier的值,只有匹配该值的qualifier的bean才能注入.
-
对于定义成list/map或者数组类型的bean是来说,@Resource是一个好的方案,通过一个唯一的名字指向特定的数组或集合.这意味着,在4.3中,集合或数组类型可以通过spring的@Autowired进行类型匹配.
-
在4.3中,@Autowired也可以考虑自注入,即引用到正在注入的bean.自注入是一个回退;常规注入仍然有优先级.也就是说,自引用不是常规候选项,因此他没有优先级;相反,他们(自注入)的优先级是最低的.实际上,使用自引用是最后的手段.例如:通过bean的事务代理调用相同bean的其他方法,在此场景下可以考虑将受影响的方法分解到单独的代理bean中.
-
@Autowired适用于字段,构造器,混成参数方法,通过@Qualifier注解来限制参数范围.相反的,@Resource注解支持字段,bean的单参数的set方法.因此,它会严格按照qualifiers来匹配你构造器或混合参数方法的注入目标.
######使用自定义的qualifier; 定义如下:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
如下使用该注解
public class MovieRecommender {
@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}
// ...
}
你可以在xml配置文件里如下配置:
<?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/>
<bean class="example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier type="example.Genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
即在<bean>标签里使用<qualifier>标签,其属性为type,value;
使用无具体值的自定义qualifier;
注解Offline定义如下:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
- 如下使用注解
public class MovieRecommender {
@Autowired
@Offline
private MovieCatalog offlineCatalog;
// ...
}
- 用Offline定义一个Bean
<bean class="example.SimpleMovieCatalog">
<qualifier type="Offline"/>
<!-- inject any dependencies required by this bean -->
</bean>
你可以自定义一个注解接受命名参数来替代简单的注解,
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
String genre();
Format format();
}
Format枚举如下:
public enum Format {
VHS, DVD, BLURAY
}
这些字段通过自定义匹配符注解来自动注入的包括以下属性的值:genre,format.
public class MovieRecommender {
@Autowired
@MovieQualifier(format=Format.VHS, genre="Action")
private MovieCatalog actionVhsCatalog;
@Autowired
@MovieQualifier(format=Format.VHS, genre="Comedy")
private MovieCatalog comedyVhsCatalog;
@Autowired
@MovieQualifier(format=Format.DVD, genre="Action")
private MovieCatalog actionDvdCatalog;
@Autowired
@MovieQualifier(format=Format.BLURAY, genre="Comedy")
private MovieCatalog comedyBluRayCatalog;
// ...
}
最后,这个bean定义必须包含匹配符的值.这个例子也说明bean的元属性可以用来替代<qualifier>的下级元素.一般情况下,<qualifier/>和它的属性有优先级,但如果没显示的匹配符,那么自动装配机制将会使用meta标签.下面的例子至少有两种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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Action"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Comedy"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<meta key="format" value="DVD"/>
<meta key="genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<meta key="format" value="BLURAY"/>
<meta key="genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>
</beans>
####7.9.5 使用泛型作为自动装配的匹配符 除了@Qualifier注解外,你还可以用java原生类型作为匹配符的一种形式,一般情况下,你需要如此配置:
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
上面的bean实现了泛型接口,比如Store<String>和Store<Integer>,你可以使用@Autowire进行注入,泛型将会作为匹配符:
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
泛型匹配符也可以用来注入List,Maps,Arrays
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
###7.9.6 CustomAutowireConfigurer(自定义自动专配配置器) 使用了CustomAutowireConfigurer(本身是一个BeanFactoryPostProcessor组件)之后,即使你的自定义qualifier没有加上@Qualifier注解,你也可以注册你的自定义qualifier;
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
AutowireCandidateResolver通过以下判断注入条件:
- 通过每个bean定义里的autowire-candidate值
- 通过<beans/>标签里的default-autowire-candidates模式
- 通过显式的@Qualifier注解和任何通过CustomAutowireConfigurer注册的自定义匹配符注解
当一个混合的beans的匹配符作为自动装配候选者,主要的候选者如下确定:假如一个候选者bean定义中设置了primary属性为true,它就会被选中.
@Resource
spring也支持使用@Resource注解来修饰字段或bean的属性设置方法.这是java EE 5和6中的公共模式,例如JSF1.2中的被管理bean或JAX-WS2.0端点.spring也支持该模式.
@Resource 提供了一个name属性,spring默认会拦截bean的名称进行过滤.也就是说,它遵循通过名称语法,就想下面例子所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
如果名字没有且明确指出,那么默认名称将来源于字段名称或set方法.如果是字段,它取字段名称;如果是set方法,它取设置的属性名称.所以以下的例子中将把名字为'movieFinder'的bean注入到这个setter方法里;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
######注 由@Resource注解提供的名字会通过CommonAnnotationBeanPostProcessor感知而被ApplicationContext处理为bean的名称.如果你明确的注册了SimpleJndiBeanFactory,这些名字会由JNDI进行处理.但是,提醒一下,你最好依赖容器默认行为并少使用spring的JNDI查找能力,这样可以保持间接的水平.
如果@Resource没有指定具体的名称,用法同@Autowired相同,@Resource会进行类型匹配以替代适配符匹配并用来解决一些常用的依赖:beanFactory,ApplicationContext,ResourceLoader,ApplicationEventPublisher,MessageSource接口.
下面的例子中,customerPerferenceDao字段首先查找一个名为customPerferenceDao的bean,如果失败则查看类型是否匹配CusstomerPreferencceDao.这个"context"字段将依据已知的依赖类型ApplicationContext进行注入.
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
####7.9.8 @PostConstruct and @PreDestroy 构造之后,销毁之前 CommonAnnotationBeanPostProcessor不仅识别@Resourece注解,也识别JSR-250中生命周期注解.在spring2.5中引入的,对这些注解的支持提供了初始化回调和销毁回调时的其他可选项.通过被spring ApplicationContext注册的CommonAnnotationBeanPostProcessor,一个带有一个或多个注解的方法可以在什么周期同一点上被调用,作为spring的生命周期接口方法或显示的申明回调方法.下面的例子中,这些缓存会再初始化之前操作,并在销毁之后清理.
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}