3.9 基于注解的容器配置
Spring框架中,注解是否优于XML配置?
基于注解的配置引入了这样一个问题:这种方法是否优于XML。简而言之,这要视情况而定。长话短说,每种方法都有其优缺点,通常由开发人员决定哪种策略更适合他们。由于它们的定义方式,注解在声明中提供了大量上下文,从而导致更短、更简洁的配置。然而,XML擅长在不接触组件源代码或重新编译它们的情况下连接组件。一些开发人员喜欢将连接靠近源,而另一些开发人员则认为带注解的类不再是pojo,而且配置变得分散,更难控制。
无论选择哪一种,Spring都可以同时容纳两种风格,甚至可以将它们混合在一起。值得指出的是,通过它的JavaConfig选项,Spring允许以一种非侵入性的方式使用注解,而不需要接触目标组件源代码,而且就工具而言,Spring工具套件支持所有配置样式。
XML设置的另一种替代方法是基于注解的配置,它依赖于字节码元数据来连接组件,而不是角括号声明。开发人员不使用XML描述bean连接,而是通过使用相关类、方法或字段声明上的注解将配置移动到组件类本身。正如在“Example: the RequiredAnnotationBeanPostProcessor”一节中提到的,将BeanPostProcessor与注解结合使用是扩展Spring IoC容器的常见方法。例如,Spring 2.0引入了使用@Required注解强制执行所需属性的可能性。Spring 2.5使得使用相同的通用方法来驱动Spring的依赖项注入成为可能。本质上,@Autowired注解提供了与3.4.5节中描述的“自动装配合作者”相同的功能,但是具有更细粒度的控制和更广泛的适用性。Spring 2.5还添加了对JSR-250注解的支持,比如@PostConstruct和@PreDestroy。Spring 3.0增加了对javax中包含的JSR-330 (Java依赖注入)注解的支持。注入包,如@Inject和@Named。有关这些注解的详细信息可以在相关部分中找到。
注意:注解注入在XML注入之前执行,因此,对于通过两种方法连接的属性,后一种配置将覆盖前者。
与往常一样,你可以将它们注册为单独的bean定义,但是也可以通过在基于xml的Spring配置中包含以下标记来隐式注册它们(请注意包含了上下文名称空间):
<?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/>只在定义bean的应用程序上下文中查找bean上的注解。这意味着,如果将< context:annotation-config/>放在DispatcherServlet的WebApplicationContext中,它只检查控制器中的@Autowired bean,而不检查服务。有关更多信息,请参见第18.2节“DispatcherServlet”。
3.9.1 @Required
@Required注解应用于bean属性setter方法,如下面的示例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
此注解仅表明必须在配置时通过bean定义中的显式属性值或自动装配注入受影响的bean属性(译者注:受影响是以来的对象)。如果未注入受影响的bean属性,容器将抛出异常;这允许出现紧急和显式的失败,避免稍后出现NullPointerException或类似的错误。仍然建议将断言放入bean类本身,例如,放入init方法。即使在容器外部使用该类,这样做也会强制执行那些必需的引用和值。
3.9.2 @Autowired
注意:在下面的例子中,JSR 330的@Inject注解可以代替Spring的@Autowired注解。详情请看这里。
你可以将@Autowired注解应用于构造函数:
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
注意:从Spring Framework 4.3开始,如果目标bean只定义一个构造函数,那么@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 {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
private MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
也可以从ApplicationContext中提供特定类型的所有bean,方法是将注解添加到期望该类型数组的字段或方法中:
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}
这同样适用于类型化集合:
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
注意:如果希望数组或列表中的项按特定顺序排序,你的bean可以实现org.springframework.core.Orderede接口或者使用@Order或标准@Priority注解。
即使是类型化的Maps也可以自动注入,只要预期的键类型是String。Map的values将包含所有预期类型的bean,键将包含相应的bean名称:
public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
默认情况下,无论何时没有可用的候选bean,自动装配都会失败;默认行为是将带注解的方法、构造函数和字段视为指示所需依赖项。可以更改此行为,如下所示。
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
注意:每个类只能标记一个带注解的构造函数,但是可以标记多个非必需的构造函数。在这种情况下,每个参数都在候选函数中考虑,Spring使用最贪婪的构造函数,它的依赖关系可以得到满足,也就是参数数量最多的构造函数。
注意:@Autowired的required属性推荐使用“@Required注解”。required属性表示该属性对于自动装配不需要,如果不能自动装配,则忽略该属性。另一方面,@Required更强大,因为它强制执行由容器支持的任何方法设置的属性。如果没有注入值,则引发相应的异常。
你还可以将@Autowired用于众所周知的可解析依赖关系的接口:BeanFactory、ApplicationContext、Environment、ResourceLoader、ApplicationEventPublisher和MessageSource。这些接口及其扩展接口(如ConfigurableApplicationContext或ResourcePatternResolver)将自动解析,无需特殊设置。
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
注意:@Autowired、@Inject、@Resource和@Value注解由Spring BeanPostProcessor实现处理,这意味着你不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型(如果有的话)中应用这些注解。这些类型必须通过XML或使用Spring @Bean方法显式地“连接”起来。
3.9.3 使用@Primary微调基于注解的自动装配
由于按类型自动装配可能导致多个候选项,因此通常需要对选择过程有更多的控制。实现这一点的一种方法是使用Spring的@Primary注解。@Primary表示,当多个bean是自动生成单值依赖项的候选bean时,应该优先考虑特定bean。如果在候选bean中只存在一个“primary”bean,那么它就是autowired值。
假设我们有以下配置,它将firstMovieCatalog定义为primary MovieCatalog。
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
在这样的配置下,firstMovieCatalog将会被自动注入到MovieRecommender中:
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
相应的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" 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>
3.9.4 使用@Qualifier微调基于注解的自动装配
@Primary是一种根据类型使用自动装配的有效方法,当可以确定一个主要候选对象时,可以在多个实例中使用自动装配。当需要对选择过程进行更多控制时,可以使用Spring的@Qualifier注解。你可以将限定符值与特定的参数关联起来,缩小类型匹配集,以便为每个参数选择特定的bean。在最简单的情况下,这可以是一个简单的描述性值:
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;
}
// ...
}
相应的bean定义如下所示。带有限定符值“main”的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名称被认为是默认的限定符值。因此,你可以使用id“main”来定义bean,而不是使用嵌套的限定符元素,从而得到相同的匹配结果。然而,尽管你可以使用这个约定通过名称引用特定的bean,但是@Autowired基本上是关于类型驱动的注入,带有可选的语义限定符。这意味着限定符值,即使使用bean名称回退,在类型匹配集中也总是具有收缩语义;他们没有语义表达独特的bean的引用id。在前面的例子,良好的限定符值是“mian”或“EMEA”或“persistent”,表达某个特定组件的特点是独立于bean id,这可能是在匿名的情况下自动生成的bean定义。
限定符也适用于类型化集合,如上所述,例如,Set集合。在本例中,所有根据声明的限定符匹配的bean都作为一个集合注入。这意味着限定符不必是惟一的;它们只是简单地构成过滤标准。例如,你可以使用相同的限定符值“action”定义多个MovieCatalog bean,所有这些bean都将被注入到一个Set集合中,该集合使用@Qualifier(“action”)注解。
注意:如果你打算通过名称来表示注解驱动的注入,那么不要主要使用@Autowired,即使在技术上能够通过@Qualifier值引用bean名。相反,使用JSR-250 @Resource注解,它的语义定义是通过特定的名称来标识特定的目标组件,声明的类型与匹配过程无关。@Autowired具有相当不同的语义:在按类型选择候选bean之后,指定的字符串限定符值将只在那些类型选择的候选对象中被考虑,例如,将一个“account”限定符与标记有相同限定符标签的bean相匹配。对于本身定义为集合/映射或数组类型的bean, @Resource是一个很好的解决方案,它通过惟一的名称引用特定的集合或数组bean。也就是说,从4.3开始,只要元素类型信息保存在@Bean返回类型签名或集合继承层次结构中,集合/映射和数组类型也可以通过Spring的@Autowired类型匹配算法进行匹配。在本例中,限定符值可用于在相同类型的集合中进行选择,如上一段所述。
注意:在4.3中,@Autowired还考虑了注入的自引用,即对当前注入的bean的引用。注意,自我注入是一种退路;对其他组件的常规依赖始终具有优先级。从这个意义上说,自我参照不参与常规的候选人选择,因此也就不是主要的;相反,它们总是以最低的优先级结束。在实践中,只使用自引用作为最后的手段,例如,通过bean的事务代理在同一个实例上调用其他方法:考虑在这种情况下将受影响的方法分解到单独的委托bean中。或者,使用@Resource,它可以通过当前bean的唯一名称获得一个代理。
@Autowired适用于字段、构造函数和多参数方法,允许通过参数级别的限定符注解来缩小范围。相反,@Resource只支持具有单个参数的字段和bean属性设置器方法。因此,如果你的注入目标是构造函数或多参数方法,请坚持使用限定符。
你可以创建自己的自定义限定符注解。只需定义一个注解并在定义中提供@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/>标记的子元素,然后指定类型和值来匹配你的自定义限定符注解。类型与注解的完全限定类名匹配。或者,为了方便,如果不存在名称冲突的风险,可以使用简短的类名。下面的示例演示了这两种方法。
<?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>
在第3.10节“类路径扫描和托管组件”中,你将看到一个基于注解的替代方法,以XML提供限定符元数据。具体地说,请参见3.10.8节,“使用注解提供限定符元数据”。
在某些情况下,使用没有值的注解就足够了。当注解具有更一般的用途,并且可以跨几种不同类型的依赖项应用时,这可能很有用。例如,你可以提供一个离线目录,当没有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>
你还可以定义自定义限定符注解,除简单值属性外,这些注解还接受已命名的属性。如果在要自动装配的字段或参数上指定了多个属性值,则bean定义必须匹配所有这些属性值,才能被视为自动装配候选。例如,考虑以下注解定义:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
String genre();
Format format();
}
在这种情况下,格式是enum:
public enum Format {
VHS, DVD, BLURAY
}
要自动生成的字段使用自定义限定符进行注解,并包含两个属性的值:类型和格式。
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/>标记中提供的限定符,则自动装配机制将返回到< 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>
3.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 Store接口,泛型将用作限定符:
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
通用限定符也适用于自动装配Lists,Mpas和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;
3.9.6 CustomAutowireConfigurer
Customautowirefigurer是一个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通过以下方法确定自动装配候选对象:
- 每个bean定义的autowire-candidate值
- < beans/>元素上可用的任何default-autowire-candidate模式
- @Qualifier注解和Customautowirefigurer注册的任何自定义注解的存在
3.9.7 @Resource
Spring还支持在字段或bean属性设置器方法上使用JSR-250 @Resource注解进行注入。这是Java EE 5和6中的常见模式,例如在JSF 1.2托管bean或JAX-WS 2.0端点中。Spring也支持Spring-managed对象的这种模式。
@Resource接受name属性,默认情况下,Spring将该值解释为要注入的bean名称。换句话说,它遵循名称语义,如下例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
如果没有显式指定名称,则默认名称派生自字段名称或setter方法。对于字段,它采用字段名;对于setter方法,它采用bean属性名。下面的例子将把名为movieFinder的bean注入setter方法:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
注意:注解提供的名称由ApplicationContext中的CommonAnnotationBeanPostProcessor解析为bean名称。如果显式配置Spring的SimpleJndiBeanFactory,则可以通过JNDI解析这些名称。但是,建议你依赖缺省行为,并简单地使用Spring的JNDI查找功能来保持间接级别。
在没有指定显式名称的@Resource usage(类似于@Autowired)的独占情况下,@Resource发现一个主类型匹配,而不是一个特定的命名bean,并解决众所周知的可解析依赖关系:BeanFactory、ApplicationContext、ResourceLoader、ApplicationEventPublisher和MessageSource接口。
因此在下面的示例中,customerPreferenceDao字段首先查找名为customerPreferenceDao的bean,然后返回到customerPreferenceDao类型的主类型匹配。“context”字段是基于已知的可解析依赖项类型ApplicationContext注入的。
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
3.9.8 @PostConstruct和@PreDestroy
CommonAnnotationBeanPostProcessor不仅可以识别@Resource注解,还可以识别JSR-250中lifeCycle注解。在Spring 2.5中引入的对这些注解的支持为初始化回调和销毁回调中描述的注解提供了另一种选择。如果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...
}
}
注意:有关组合各种生命周期机制的效果的详细信息,请参阅“组合生命周期机制”一节。