译自Spring官方文档关于IOC容器章节
出处:https://docs.spring.io/spring/docs/4.3.9.RELEASE/spring-framework-reference/html/beans.html#beans-introduction
关于Spring IOC容器了解更多:
本章介绍了Spring Framework实现的控制反转(IoC)[1]原理。IoC也称为依赖注入(DI)。这是一个过程,通过这个过程,对象定义它们的依赖关系,即它们使用的其他对象,只能通过构造函数参数,工厂方法的参数,或者在构造或从工厂方法返回后在对象实例上设置的属性。 。然后容器 在创建bean时注入这些依赖项。这个过程基本上是反向的,因此名称Inversion of Control(IoC),bean本身通过使用类的直接构造来控制其依赖关系的实例化或位置,或者诸如服务定位器模式。
在org.springframework.beans
和org.springframework.context
包是Spring框架的IoC容器的基础。该 BeanFactory
接口提供了一种能够管理任何类型对象的高级配置机制。 ApplicationContext
是一个子界面BeanFactory
。它增加了与Spring的AOP功能的更容易的集成; 消息资源处理(用于国际化),事件发布; 和特定于应用程序层的上下文,例如WebApplicationContext
在Web应用程序中使用的上下文。
简而言之,它BeanFactory
提供了配置框架和基本功能,并ApplicationContext
添加了更多企业特定的功能。它ApplicationContext
是完整的超集,BeanFactory
在本章中专门用于Spring的IoC容器的描述。有关使用BeanFactory
而不是ApplicationContext,
参考 第7.16节“BeanFactory”的更多信息。
在Spring中,构成应用程序主干并由Spring IoC 容器管理的对象称为bean。bean是一个由Spring IoC容器实例化,组装和管理的对象。否则,bean只是应用程序中众多对象之一。Bean及其之间的依赖 关系反映在容器使用的配置元数据中。
该接口org.springframework.context.ApplicationContext
代表Spring IoC容器,负责实例化,配置和组装上述bean。容器通过读取配置元数据获取有关要实例化,配置和组装的对象的指令。配置元数据以XML,Java注释或Java代码表示。它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖性。
ApplicationContext
Spring的开箱即用的几个接口实现。在独立应用程序中,通常创建ClassPathXmlApplicationContext
或的实例FileSystemXmlApplicationContext
。虽然XML是定义配置元数据的传统格式,但您可以通过提供少量XML配置来声明性地支持这些其他元数据格式,从而指示容器使用Java注释或代码作为元数据格式。
在大多数应用程序方案中,不需要显式用户代码来实例化Spring IoC容器的一个或多个实例。例如,在Web应用程序场景中,应用程序文件中的简单八行(左右)样板Web描述符XML web.xml
通常就足够了(请参见第7.15.4节“Web应用程序的便捷ApplicationContext实例化”)。如果您使用的是 Spring工具套件 Eclipse驱动的开发环境,只需点击几下鼠标或按键即可轻松创建此样板文件配置。
下图是Spring工作原理的高级视图。您的应用程序类与配置元数据相结合,以便在ApplicationContext
创建和初始化之后,您拥有完全配置且可执行的系统或应用程序。
图7.1。Spring IoC容器
注释是否比配置Spring的XML更好?
基于注释的配置的引入引发了这种方法是否比XML更“好”的问题。简短的答案取决于它。答案很长,每种方法都有其优点和缺点,通常由开发人员决定哪种策略更适合他们。由于它们的定义方式,注释在其声明中提供了大量上下文,从而导致更短更简洁的配置。但是,XML擅长在不触及源代码或重新编译它们的情况下连接组件。一些开发人员更喜欢将布线靠近源,而另一些开发人员则认为注释类不再是POJO,而且配置变得分散且难以控制。
无论选择如何,Spring都可以兼顾两种风格,甚至可以将它们混合在一起。值得指出的是,通过其JavaConfig选项,Spring允许以非侵入方式使用注释,而无需触及目标组件源代码,并且在工具方面,Spring Tool Suite支持所有配置样式 。
基于注释的配置提供了XML设置的替代方案,该配置依赖于字节码元数据来连接组件而不是角括号声明。开发人员不是使用XML来描述bean连接,而是通过在相关的类,方法或字段声明上使用注释将配置移动到组件类本身。正如在“示例:RequiredAnnotationBeanPostProcessor”一节中所提到的BeanPostProcessor
,结合注释是扩展Spring IoC容器的常用方法。例如,Spring 2.0引入了使用@Required强制执行所需属性的可能性注解。Spring 2.5使得有可能采用相同的通用方法来驱动Spring的依赖注入。本质上,@Autowired
注释提供的功能与第7.4.5节“自动装配协作者”中所述的功能相同,但具有更细粒度的控制和更广泛的适用性。Spring 2.5还增加了对JSR-250注释的支持,例如 @PostConstruct
和@PreDestroy
。Spring 3.0增加了对javax.inject包中包含的JSR-330(Java的依赖注入)注释的支持,例如@Inject
和@Named
。有关这些注释的详细信息,请参阅 相关章节。
注释注入在 XML注入之前执行,因此后一种配置将覆盖通过两种方法连接的属性的前者。 |
与往常一样,您可以将它们注册为单独的bean定义,但也可以通过在基于XML的Spring配置中包含以下标记来隐式注册它们(请注意包含context
命名空间):
(该隐式注册的后处理器包括 AutowiredAnnotationBeanPostProcessor
, CommonAnnotationBeanPostProcessor
,PersistenceAnnotationBeanPostProcessor
,以及前述 RequiredAnnotationBeanPostProcessor
)。
|
该@Required
注释适用于bean属性setter方法,如下面的例子:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
此注释仅指示必须在配置时通过bean定义中的显式属性值或通过自动装配填充受影响的bean属性。如果尚未填充受影响的bean属性,容器将引发异常; 这允许急切和明确的失败,以后避免NullPointerException
s等。仍然建议您将断言放入bean类本身,例如,放入init方法。即使您在容器外使用类,这样做也会强制执行那些必需的引用和值。
在下面的示例中, |
您可以将@Autowired
注释应用于构造函数:
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
从Spring Framework 4.3开始, |
正如所料,您还可以将@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;
}
// ...
}
通过将注释添加到需要该类型数组的字段或方法,也可以提供特定类型的所有 bean ApplicationContext
:
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}
这同样适用于类型集合:
public class MovieRecommender {
private Set movieCatalogs;
@Autowired
public void setMovieCatalogs(Set movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
如果希望将数组或列表中的项目排序为特定顺序,则Bean可以实现 |
即使键入的地图也可以自动装配,只要预期的密钥类型是String
。Map值将包含所需类型的所有bean,并且键将包含相应的bean名称:
public class MovieRecommender {
private Map movieCatalogs;
@Autowired
public void setMovieCatalogs(Map movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
默认情况下,只要零候选bean可用,自动装配就会失败; 默认行为是将带注释的方法,构造函数和字段视为指示所需的依赖项。可以更改此行为,如下所示。
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
只有一个每类注释构造函数可以作为标记要求,但多个非必需的构造函数可以被注解。在这种情况下,每个都被认为是候选者之一,Spring使用最贪婪的构造函数,其依赖性可以得到满足,即具有最多参数的构造函数。
|
您还可以使用@Autowired
对于那些众所周知的解析依赖接口:BeanFactory
,ApplicationContext
,Environment
,ResourceLoader
,ApplicationEventPublisher
,和MessageSource
。这些接口及其扩展接口(如ConfigurableApplicationContext
或ResourcePatternResolver
)会自动解析,无需特殊设置。
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
|
由于按类型自动装配可能会导致多个候选人,因此通常需要对选择过程有更多控制权。实现这一目标的一种方法是使用Spring的 @Primary
注释。@Primary
表示当多个bean可以自动装配到单值依赖项时,应该优先选择特定的bean。如果候选者中只存在一个“主”bean,则它将是自动装配的值。
假设我们有以下配置定义firstMovieCatalog
为 主要 配置MovieCatalog
。
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
通过这样的配置,以下MovieRecommender
将自动装配 firstMovieCatalog
。
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
相应的bean定义如下所示。
@Primary
当可以确定一个主要候选者时,是通过具有多个实例的类型使用自动装配的有效方式。当需要更多地控制选择过程时,@Qualifier
可以使用Spring的注释。您可以将限定符值与特定参数相关联,缩小类型匹配集,以便为每个参数选择特定的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与使用相同值限定的构造函数参数连接。
对于回退匹配,bean名称被视为默认限定符值。因此,您可以使用id“main”而不是嵌套的限定符元素来定义bean,从而得到相同的匹配结果。但是,虽然您可以使用此约定来按名称引用特定bean,但@Autowired
基本上是关于具有可选语义限定符的类型驱动注入。这意味着即使使用bean名称回退,限定符值在类型匹配集中也总是具有缩小的语义; 它们在语义上不表示对唯一bean id的引用。好的限定符值是“主要”或“EMEA”或“持久性”,表示独立于bean的特定组件的特征id
,
限定符也适用于类型集合,如上所述,例如, Set
。在这种情况下,根据声明的限定符的所有匹配bean都作为集合注入。这意味着限定符不必是唯一的; 它们只是简单地构成过滤标准。例如,您可以MovieCatalog
使用相同的限定符值“action” 定义多个bean,所有这些bean都将注入带Set
注释的注释中@Qualifier("action")
。
如果您打算按名称表达注释驱动的注入,请不要主要使用 对于本身定义为集合/映射或数组类型的 从4.3开始,
|
您可以创建自己的自定义限定符注释。只需定义注释并@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定义的信息。您可以将
标记添加为 标记的子元素,
然后指定type
和 value
匹配自定义限定符注释。该类型与注释的完全限定类名匹配。或者,为方便起见,如果不存在冲突名称的风险,您可以使用短类名。以下示例演示了这两种方法。
在第7.10节“类路径扫描和托管组件”中,您将看到基于注释的替代方法,即以XML格式提供限定符元数据。具体来说,请参见第7.10.8节“使用注释提供限定符元数据”。
在某些情况下,使用没有值的注释可能就足够了。当注释用于更通用的目的并且可以跨多种不同类型的依赖项应用时,这可能很有用。例如,您可以提供 在没有Internet连接时将搜索的脱机目录。首先定义简单注释:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
然后将注释添加到要自动装配的字段或属性中:
public class MovieRecommender {
@Autowired
@Offline
private MovieCatalog offlineCatalog;
// ...
}
现在bean定义只需要一个限定符type
:
您还可以定义除简单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 元属性而不是
子元素。如果可用,则
其属性优先,但如果不存在此类限定符,则自动装配机制将回退到标记中提供的值 ,如以下示例中的最后两个bean定义。
除了@Qualifier
注释之外,还可以使用Java泛型类型作为隐式的限定形式。例如,假设您具有以下配置:
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
假设上述bean实现一个通用接口,即Store
和 Store
,你可以@Autowire
在Store
界面和通用将作为一个限定:
@Autowired
private Store s1; // qualifier, injects the stringStore bean
@Autowired
private Store s2; // qualifier, injects the integerStore bean
通用限定符也适用于自动装配列表,地图和数组:
// Inject all Store beans as long as they have an generic
// Store beans will not appear in this list
@Autowired
private List> s;
这 CustomAutowireConfigurer
是一个BeanFactoryPostProcessor
使您能够注册自己的自定义限定符注释类型,即使它们没有使用Spring的@Qualifier
注释注释。
example.CustomQualifier
通过以下方式AutowireCandidateResolver
确定autowire候选人:
autowire-candidate
每个bean定义 的值default-autowire-candidates
可用的 任何模式
@Qualifier
注释 的存在以及注册的任何自定义注释CustomAutowireConfigurer
当多个bean有资格作为autowire候选者时,“primary”的确定如下:如果候选者中只有一个bean定义具有primary
设置为的属性true
,则将选择它。
Spring还支持@Resource
在字段或bean属性setter方法上使用JSR-250 注释进行注入。这是Java EE 5和6中的常见模式,例如在JSF 1.2托管bean或JAX-WS 2.0端点中。Spring也支持Spring管理对象的这种模式。
@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;
}
}
提供注解的名称解析由一个bean的名称 |
在专属情况下,@Resource
不指定明确的名称,以及类似的使用@Autowired
,@Resource
发现的主要类型的比赛,而不是一个具体的bean并解决众所周知的解析依存关系:BeanFactory
, ApplicationContext
,ResourceLoader
,ApplicationEventPublisher
,和MessageSource
接口。
因此,在以下示例中,customerPreferenceDao
字段首先查找名为customerPreferenceDao的bean,然后返回到该类型的主类型匹配 CustomerPreferenceDao
。基于已知的可解析依赖性类型注入“上下文”字段ApplicationContext
。
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
将CommonAnnotationBeanPostProcessor
不仅承认了@Resource
注解也是JSR-250 的生命周期注解。在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...
}
}
使用@Import注释
就像
在Spring XML文件中使用元素来帮助模块化配置一样,@Import
注释允许@Bean
从另一个配置类加载定义:
@Configuration
public class ConfigA {
@Bean
public A a() {
return new
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new
}
}
现在,不需要同时指定ConfigA.class
和ConfigB.class
实例化上下文,只ConfigB
需要显式提供:
public static void
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
这种方法简化了容器实例化,因为只需要处理一个类,而不是要求开发人员@Configuration
在构造期间记住可能大量的 类。
从Spring Framework 4.2开始, |
注入对导入的@Bean定义的依赖关系
上面的例子有效,但很简单。在大多数实际情况中,bean将跨配置类相互依赖。使用XML时,这本身并不是问题,因为不涉及编译器,可以简单地声明ref="someBean"
并相信Spring会在容器初始化期间解决它。当然,在使用@Configuration
类时,Java编译器会对配置模型施加约束,因为对其他bean的引用必须是有效的Java语法。
幸运的是,解决这个问题很简单。正如我们已经讨论过的, @Bean
方法可以有任意数量的参数来描述bean的依赖关系。让我们考虑一个更真实的场景,其中有几个@Configuration
类,每个类都依赖于在其他类中声明的bean:
@Configuration
public class ServiceConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
@Bean
public AccountRepository accountRepository(DataSource dataSource) {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
还有另一种方法可以达到相同的效果。请记住,@Configuration
类最终只是容器中的另一个bean:这意味着它们可以像任何其他bean一样利用 @Autowired
和@Value
注入等等!
确保以这种方式注入的依赖项只是最简单的。 另外,要特别注意 |
@Configuration
public class ServiceConfig {
@Autowired
private AccountRepository accountRepository;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
private final DataSource dataSource;
@Autowired
public RepositoryConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
|
在上面的场景中,使用@Autowired
效果很好并提供了所需的模块性,但确定声明自动装配的bean定义的确切位置仍然有些模棱两可。例如,作为开发人员ServiceConfig
,您如何确切地知道@Autowired AccountRepository
bean的声明位置?它在代码中并不明确,这可能就好了。请记住, Spring Tool Suite提供的工具可以呈现图形,显示所有内容的连接方式 - 这可能就是您所需要的。此外,您的Java IDE可以轻松找到该AccountRepository
类型的所有声明和用法,并将快速显示@Bean
返回该类型的方法的位置。
如果这种歧义是不可接受的,并且您希望从IDE中直接从一个@Configuration
类导航到另一个类,请考虑自行装配配置类:
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
// navigate 'through' the config class to the @Bean method!
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
在上面的情况中,它是完全明确AccountRepository
的定义。但是,ServiceConfig
现在紧紧地联系在一起RepositoryConfig
; 这是权衡。通过使用基于接口的类或基于@Configuration
类的抽象类,可以在某种程度上减轻这种紧密耦合。考虑以下:
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
@Configuration
public interface RepositoryConfig {
@Bean
AccountRepository accountRepository();
}
@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(...);
}
}
@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config!
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
现在ServiceConfig
与具体的松散耦合 DefaultRepositoryConfig
,内置的IDE工具仍然很有用:开发人员很容易获得实现的类型层次结构RepositoryConfig
。通过这种方式,导航@Configuration
类及其依赖关系与导航基于接口的代码的常规过程没有什么不同。
有条件地包括@Configuration类或@Bean方法
基于某些任意系统状态,有条件地启用或禁用完整@Configuration
类或甚至单个@Bean
方法通常很有用。一个常见的例子是@Profile
只有在Spring中启用了特定的配置文件时才使用注释来激活bean Environment
( 有关详细信息,请参见第7.13.1节“Bean定义配置文件”)。
该@Profile
注释是使用所谓的更灵活的注释实际执行@Conditional
。该@Conditional
注释指示特定 org.springframework.context.annotation.Condition
前应谘询的实施@Bean
是注册。
Condition
接口的实现只是提供一个matches(…)
返回true
或的方法false
。例如,以下是Condition
用于的实际 实现@Profile
:
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
// Read the @Profile annotation attributes
MultiValueMap attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
有关更多详细信息,请参阅javadocs。 @Conditional
结合Java和XML配置
Spring的@Configuration
类支持并非旨在成为Spring XML的100%完全替代品。诸如Spring XML命名空间之类的一些工具仍然是配置容器的理想方式。在XML方便或必要的情况下,您可以选择:例如,以“以XML为中心”的方式实例化容器 ClassPathXmlApplicationContext
,或者以“以Java为中心”的方式使用AnnotationConfigApplicationContext
和@ImportResource
根据需要导入XML 的注释。 。
以XML为中心的@Configuration类的使用
最好从XML引导Spring容器并@Configuration
以ad-hoc方式包含 类。例如,在使用Spring XML的大型现有代码库中,根据需要创建@Configuration
类并将其包含在现有XML文件中会更容易。下面你将找到@Configuration
在这种“以XML为中心”的情况下使用类的选项。
请记住,@Configuration
类最终只是容器中的bean定义。在这个例子中,我们创建了一个@Configuration
名为的类,AppConfig
并将其system-test-config.xml
作为
定义包含在内。因为
已打开,容器将识别 @Configuration
注释并 正确处理@Bean
声明的方法AppConfig
。
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
system-test-config.xml:
jdbc.properties:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
在 |
因为@Configuration
带有元注释@Component
,注释@Configuration
类自动成为组件扫描的候选者。使用与上面相同的方案,我们可以重新定义system-test-config.xml
以利用组件扫描。请注意,在这种情况下,我们不需要显式声明
,因为
启用相同的功能。
system-test-config.xml:
@Configuration以类为中心使用带@ImportResource的XML
在@Configuration
类是配置容器的主要机制的应用程序中,仍然可能需要使用至少一些XML。在这些场景中,只需使用@ImportResource
和定义所需的XML。这样做可以实现“以Java为中心”的方法来配置容器并将XML保持在最低限度。
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
properties-config.xml
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}