您的位置:首页 > 其它

基于注解(Annotation-based)的配置

2009-12-02 16:33 435 查看

Spring 2.0 对必须的属性引入了@Required注解。在 Spring 2.5中已经可以用注解的方式去驱动 Spring 的依赖注射了。更重要的是,
@Autowired
注解提供了与第 3.3.5 节 “自动装配(autowire)协作者”一节中描述的同样功能,并且提供了更细致的控制与更好的适应性。Spring 2.5 也支持 JSR-250 中的一些注解,例如
@Resource
@PostConstruct
,以及
@PreDestroy
。当然,要使注解可用,您必须使用 Java 5 (Tiger)或更新的版本,以使得可以访问源代码层次的注解。这些注解可以被注册为独立 bean 的定义,但它们也可以被隐式地注册,通过基于 XML 的配置方式,如下例(请注意包含 '
context
' 命名空间):

<?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:annotation-config/>

</beans>

(隐式注册 post-processors 包括了
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
,也包括了前面提到的
RequiredAnnotationBeanPostProcessor
。)

3.11.1.
@Autowired

@Autowired
注解可以用于“传统的”setter 方法,如下例:

public class SimpleMovieLister {

private MovieFinder movieFinder;

@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...
}

这个注解也可以用于以属性为参数/多个参数的方法

public class MovieRecommender {

private MovieCatalog movieCatalog;

private CustomerPreferenceDao customerPreferenceDao;

@Autowired
public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}

// ...
}

@Autowired
注解甚至可以用于构造器与字段:

public class MovieRecommender {

@Autowired
private MovieCatalog movieCatalog;

private CustomerPreferenceDao customerPreferenceDao;

@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}

// ...
}

也可以一种提供来自
ApplicationContext
的特殊类型的所有 beans,注解字段或者方法,例如:

public class MovieRecommender {

@Autowired
private MovieCatalog[] movieCatalogs;

// ...
}

这同样适用于集合类型:

public class MovieRecommender {

private Set<MovieCatalog> movieCatalogs;

@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}

// ...
}

甚至是 Maps 也可以这样注解,只要这个 Map 的 key 类型为
String
。这个 Map 的 values 应该是已知的类型,并且 keys 应该包含符合 bean 的命名:

public class MovieRecommender {

private Map<String, MovieCatalog> movieCatalogs;

@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}

// ...
}

在缺省情况下,当出现0个候选的 beans时自动连接将失败;缺省行为把连接方法,构造器,字段假设为 required 的依赖。这样的行为如下所示:

public class SimpleMovieLister {

private MovieFinder movieFinder;

@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}

// ...
}


注意

虽然当 一个类只有一个连接构造器时它将被标记为 required, 但是还是可以标记多个构造器的。在这种情况下,每一个构造器都有可能被认为是连接构造器, Spring 将会把依赖关系能够满足的构造器认为是greediest 的构造器。

@Autowired
也能用于总所周知的“可解决的依赖”:
BeanFactory
接口,
ApplicationContext
接口,
ResourceLoader
接口,
ApplicationEventPublisher
接口,还有
MessageSource
接口。这些接口(还有它们的扩展,例如
ConfigurableApplicationContext
或者
ResourcePatternResolver
)将可以自动解决依赖,没有任何特殊必须的其它步骤需要。

public class MovieRecommender {

@Autowired
private ApplicationContext context;

public MovieRecommender() {
}

// ...
}


3.11.2. 基于注解的自动连接微调

因为通过类型的自动连接可能会有多个候选,因此经常需要在选择过程中加以控制。一种方法去完成这个控制就是使用
@Qualifier
注解。在最简单的情况下,您能够通过命名方式去实现这个自动连接:

public class MovieRecommender {

@Autowired
@Qualifier("mainCatalog")
private MovieCatalog movieCatalog;

// ...
}

@Qualifier
注解也能够被指定为构造器的参数或者方法的参数:

public class MovieRecommender {

private MovieCatalog movieCatalog;

private CustomerPreferenceDao customerPreferenceDao;

@Autowired
public void prepare(@Qualifier("mainCatalog") MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}

// ...
}

您也可以创建您自定义的限定器注解。您只要在定义一个注解时提供
@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;
}

// ...
}

下一步就是提供信息给候选的 bean 的定义。您能够添加
<qualifier/>
标签作为
<bean/>
标签的子元素,然后指定
'type'
还有
'value'
以匹配您的自定义限定器注解。类型必须匹配注解的全名,或者是一个不危险的、方便一点的名字,您也可以使用“短” 类名。参看下例:

<?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: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>

在下一节,题目是第 3.12 节 “对受管组件的Classpath扫描”,您将看到使用XML提供给限定器元数据且基于注解的可选解决方案。特别地,请参看:第 3.12.6 节 “用注解提供限定符元数据”

在某些情况下,有足够充分的理由去使用不带值的注解。这使得注解可以提供更多解决不同类型依赖的能力。例如,在 Internet 连接不可用时,您可以提供一个离线的搜索目录。首先就要定义一个简单的注解:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}

然后添加这个注解给字段作为自动连接:

public class MovieRecommender {

@Autowired
@Offline
private MovieCatalog offlineCatalog;

// ...
}

现在,这个 bean 的定影只组要一个限定器了:

<bean class="example.SimpleMovieCatalog">
<qualifier type="Offline"/>
<!-- inject any dependencies required by this bean -->
</bean>

另外,也可以定制自己的限定器注解去使用命名的属性或者简单的
'value'
属性。如果自动连接时多个属性值被指定给了一个字段或者参数,那么一个 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/>
以及它的属性将优先考虑,但是如果没有限定器的话(参看如下定义的后两个 bean ),自动连接机制将取消以
<meta/>
标签标记的值。

<?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:annotation-config/>

<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute name="format" value="VHS"/>
<attribute name="genre" value="Action"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>

<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute name="format" value="VHS"/>
<attribute name="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>


3.11.3.
CustomAutowireConfigurer

CustomAutowireConfigurer
是一个
BeanFactoryPostProcessor
,它可以使得在自动连接过程中做更多的自定义选择。特殊地,它允许您注册您自己的自定义限定器注解类型,甚至是它们没有使用 Spring 的
@Qualifier
注解标注它们自己。

<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>

请注意,
AutowireCandidateResolver
的实现将依赖于 Java 版本。如果在 Java 5 以下,限定器注解是不被支持的,因此自动连接后选将被
'autowire-candidate'
的值或者在
<beans/>
中元素
'default-autowire-candidates'
可用的模式所决定。如果运行在 Java 5 或者更新的版本上,
@Qualifier
注解或者任何自定义并在
CustomAutowireConfigurer
上注册过的注解都将正常工作。

忽略 Java 版本,决定“主要”的后选(当多个 beans 都配置为自动连接后选时)都是一样的:在这些后选中只要一个 bean 的
'primary'
属性定义为
'true'
即可。

3.11.4.
@Resource

Spring 也提供了使用 JSR-250 bean 属性支持的注射方式。这是一种在 Java EE 5 与 Java 6 中普遍使用的方式(例如,在 JSF 1.2 中映射 beans 或者 JAX-WS 2.0 端点),对于Spring 托管的对象 Spring 可以以这种方式支持映射。

@Resource
有一个‘name’属性,缺省时,Spring 将这个值解释为要注射的 bean 的名字。换句话说,如果遵循by-name的语法,如下例:

public class SimpleMovieLister {

private MovieFinder movieFinder;

@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}

如果没有显式地给出名字,缺省的名字将继承于字段名或者 setter 方法名:如果是字段名,它将简化或者等价于字段名;如果是 setter 方法名,它将等价于 bean 属性名。下面这个例子使用名字 "movieFinder" 注射到它的 setter 方法:

public class SimpleMovieLister {

private MovieFinder movieFinder;

@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}


注意

注解提供的名字将被
BeanFactory
解析为 bean 名。请注意,这些名字也可能通过 JNDI 被解析(需要配置 Spring 的
SimpleJndiBeanFactory
)。不过,建议您依靠缺省行为与 Spring 的 JNDI 查找功能。

@Autowired
类似,
@Resource
可以回退为与标准 bean 类型匹配(例如,使用原始类型匹配取代特殊命名 bean)来解决著名的"resolvable dependencies":
BeanFactory
接口,
ApplicationContext
接口,
ResourceLoader
接口,
ApplicationEventPublisher
接口以及
MessageSource
接口。请注意:这只有适用于未指定命名的
@Resource


下面的例子有一个
customerPreferenceDao
字段,首先要查找一个名叫 “customerPreferenceDao” 的 bean,然后回退为一个原始类型以匹配类型
CustomerPreferenceDao
。"context" 字段将基于已知解决的依赖类型
ApplicationContext
而被注入。

public class MovieRecommender {

@Resource
private CustomerPreferenceDao customerPreferenceDao;

@Resource
private ApplicationContext context;

public MovieRecommender() {
}

// ...
}


3.11.5.
@PostConstruct
@PreDestroy

CommonAnnotationBeanPostProcessor
不只是能识别
@Resource
注解,而且也能识别 JSR-250 lifecycle注解。在 Spring 2.5 中,这些注解的描述请参看initialization callbacksdestruction callbacks节。
CommonAnnotationBeanPostProcessor
已经在 Spring 的
ApplicationContext
中注册,当一个方法带有这些注解之一时,将被在其生命周期与 Spring 生命周期接口的方法或者显式声明回调方法同一刻上调用。下面的例子里,缓存将预置于初始化与销毁阶段。

public class CachingMovieLister {

@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}

@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}


注意

关于组合不同的生命周期机制,请查看第 3.5.1.4 节 “组合生命周期机制”
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: