目录
基于注解的容器配置
在配置 Spring 时,注解是否比 XML 更好?
基于注释的配置的引入提出了这种方法是否比 XML“更好”的问题。简短的回答是“视情况而定”。长答案是每种方法都有其优点和缺点,通常由开发人员决定哪种策略更适合他们。由于它们的定义方式,注释在其声明中提供了大量上下文,从而使配置更短、更简洁。然而,XML 擅长在不触及源代码或重新编译它们的情况下连接组件。一些开发人员更喜欢在源附近进行布线,而另一些开发人员则认为带注释的类不再是 POJO,此外,配置变得分散且更难控制。
无论选择如何,Spring 都可以同时适应这两种风格,甚至可以将它们混合在一起。值得指出的是,通过其JavaConfig选项,Spring 允许以非侵入性的方式使用注释,而无需触及目标组件的源代码,并且在工具方面
基于注释的配置提供了 XML 设置的替代方案,它依赖于字节码元数据来连接组件,而不是尖括号声明。开发人员不使用 XML 来描述 bean 连接,而是通过在相关类、方法或字段声明上使用注释将配置移动到组件类本身
@Required(弃用)
注释适用于 bean 属性设置方法,@Required
如下例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
此注解指示必须在配置时通过 bean 定义中的显式属性值或通过自动装配来填充受影响的 bean 属性。如果受影响的 bean 属性尚未填充,则容器将引发异常。
注意:从Spring Framework 5.1 开始正式弃用@Required
注释,支持使用构造函数注入进行所需设置
@Autowired
您可以将@Autowired
注释应用于构造函数
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
您还可以将@Autowired
注解应用于传统的setter 方法
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
您还可以将注释应用于具有任意名称和多个参数的方法
您也可以应用于@Autowired
字段,甚至可以将其与构造函数混合使用
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
private MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
默认行为是将带注释的方法和字段视为指示所需的依赖项。您可以按照以下示例所示更改此行为,使框架能够通过将其标记为非必需(即,通过将required
属性设置@Autowired
为false
)来跳过不可满足的注入点
如果非必需方法的依赖项(或其依赖项之一,如果有多个参数)不可用,则根本不会调用非必需方法。在这种情况下,根本不会填充非必填字段,而保留其默认值。
您可以通过 Java 8 表达特定依赖项的非必需性质java.util.Optional
,如以下示例所示:
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
@Primary
由于按类型自动装配可能会导致多个候选者,因此通常需要对选择过程进行更多控制。
@Primary
指示当多个 bean 是自动装配到单值依赖项的候选对象时,应该优先考虑特定的 bean。如果候选中恰好存在一个主 bean,则它将成为自动装配的值。
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
使用上述配置,以下MovieRecommender
内容与 自动装配 firstMovieCatalog
:
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
@Qualifier
@Primary
当可以确定一个主要候选者时,是一种通过类型使用多个实例的自动装配的有效方法。当您需要对选择过程进行更多控制时,可以使用 Spring 的@Qualifier
注解。您可以将限定符值与特定参数相关联,缩小类型匹配的范围,以便为每个参数选择特定的 bean。
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
使用泛型作为自动装配限定符
您还可以使用 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
@Resource
@Resource
采用名称属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
如果没有明确指定名称,则默认名称派生自字段名称或 setter 方法。如果是字段,则采用字段名称。对于 setter 方法,它采用 bean 属性名称。
在没有指定显式名称的排他性@Resource
使用情况下,类似于@Autowired
,,则自动按byName方式进行查找。如果没有找到符合的bean,则回退为一个原始类型进行查找,如果找到就注入。
@Value
@Value
通常用于注入外部属性:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
以及以下application.properties
文件:
catalog.name=MovieCatalog
在这种情况下,catalog
参数和字段将等于该MovieCatalog
值。
原理:Spring 提供了一个默认的宽松嵌入式值解析器。它将尝试解析属性值,如果无法解析,属性名称(例如${catalog.name}
)将作为值注入。如果要严格控制不存在的值,则应声明一个PropertySourcesPlaceholderConfigurer
。
@Configuration
public class AppConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
${}
如果无法解析任何占位符,使用上述配置可确保 Spring 初始化失败。
Spring 提供的内置转换器支持允许自动处理简单的类型转换。
Spring在后台BeanPostProcessor
处理将值转换为目标类型的过程。如果您想为您自己的自定义类型提供转换支持,您可以提供您自己的 bean 实例
@Configuration
public class AppConfig {
@Bean
public ConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new MyCustomConverter());
return conversionService;
}
}
使用@PostConstruct
和@PreDestroy
生命周期
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}