####7.9基于注解的容器配置 ####前言:注解配置好仍是xml配置好? 两者各有千秋.java
在字节码文件里注入组件而不是角括号申明.开发者能够将注解添加在相关的类,方法,属性声明上历来注入依赖组件.使用bean后处理器来糅合注解是spring ioc 容器扩展的通用方式.例如,spring2.0引用了强制要求的@Required注解.值得一提的是,@Autowired注解提供了和7.4.5部分中的"Autowiring collaborators"相同的做用功能,但具备更好的控制和更广的应用.@PostConstruct,@PreDestroy,@Inject,@Namedspring
你能够在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方法;app
public class SimpleMovieLister { private MovieFinder movieFinder; @Required public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // ... }
这个注解申明其标注的属性必须在配置时间被操做,经过一些指定值或经过自动装配.若是属性没有被操做,spring启动会有明确的失败,以免以后的空指针异常.使用它能够保障必须引用.工具
####7.9.2 @Autowired @Inject注解能够替代@Autowired注解. 你可使用@Inject注解来替代@Autowired注解;ui
能够在构造器中使用@Autowired注解this
public class MovieRecommender { private final CustomerPreferenceDao customerPreferenceDao; @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; } // ... }
在spring4.3中,bean若是只定义了一个构造器,@Autowired注解没有使用必要.多个构造器才必须这么作.代理
通常而言,若是没有可选的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控制的另外一个工具.
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; // ... }
<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经过如下判断注入条件:
当一个混合的beans的匹配符做为自动装配候选者,主要的候选者以下肯定:假如一个候选者bean定义中设置了primary属性为true,它就会被选中.
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... } }