This section covers how to use annotations in your Java code to configure the Spring container. It includes the following topics: 本节介绍如何在 Java 代码中使用注解来配置 Spring 容器。包括以下主题:
@Bean and@Configuration1.12.1. 基本概念: @Bean 和 @Configuration
The central artifacts in Spring’s new Java-configuration support are @Configuration-annotated classes and @Bean
-annotated methods.
Spring 新 Java 配置支持的核心工件是 @Configuration 注解类和 @Bean 注解方法。
The @Bean annotation is used to indicate that a method instantiates, configures, and initializes a new object to
be
managed by the Spring IoC container. For those familiar with Spring’s <beans/> XML configuration, the @Bean
annotation plays the same role as the <bean/> element. You can use @Bean-annotated methods with any Spring
@Component. However, they are most often used with @Configuration beans.
@Bean 注解用于指示一个方法实例化、配置和初始化一个由 Spring IoC 容器管理的新对象。对于那些熟悉 Spring 的 <beans/> XML
配置的人来说, @Bean 注解与 <bean/> 元素起着相同的作用。你可以使用任何 Spring @Component 的 @Bean -注解方法。然而,它们最常与
@Configuration 面包一起使用。
Annotating a class with @Configuration indicates that its primary purpose is as a source of bean definitions.
Furthermore, @Configuration classes let inter-bean dependencies be defined by calling other @Bean methods in the
same class. The simplest possible @Configuration class reads as follows:
使用 @Configuration 注解一个类表示其主要目的是作为 bean 定义的来源。此外, @Configuration 类允许通过调用同一类中的其他
@Bean 方法来定义 bean 之间的依赖关系。最简单的 @Configuration 类如下所示:
The preceding AppConfig class is equivalent to the following Spring <beans/> XML:
前一个 AppConfig 类等价于以下 Spring <beans/> XML:
The @Bean and @Configuration annotations are discussed in depth in the following sections. First, however, we
cover
the various ways of creating a spring container by using Java-based configuration.
@Bean 和 @Configuration 注解在以下章节中进行了深入讨论。然而,首先,我们将介绍使用基于 Java 的配置创建 Spring
容器的各种方法。
AnnotationConfigApplicationContext
1.12.2. 通过使用 AnnotationConfigApplicationContext 实例化 Spring 容器
The following sections document Spring’s AnnotationConfigApplicationContext, introduced in Spring 3.0. This
versatile
ApplicationContext implementation is capable of accepting not only @Configuration classes as input but also
plain
@Component classes and classes annotated with JSR-330 metadata.
以下章节记录了 Spring 的 AnnotationConfigApplicationContext ,该功能自 Spring 3.0 版本引入。这个多功能的
ApplicationContext 实现不仅能够接受 @Configuration 类作为输入,还能够接受普通 @Component 类以及带有 JSR-330
元数据的类。
When @Configuration classes are provided as input, the @Configuration class itself is registered as a bean
definition and all declared @Bean methods within the class are also registered as bean definitions.
当提供 @Configuration 类作为输入时, @Configuration 类本身被注册为 bean 定义,该类中声明的所有 @Bean 方法也被注册为
bean 定义。
When @Component and JSR-330 classes are provided, they are registered as bean definitions, and it is assumed that
DI
metadata such as @Autowired or @Inject are used within those classes where necessary.
当提供 @Component 和 JSR-330 类时,它们被注册为 Bean 定义,并假定在这些类中必要时使用 DI 元数据,如 @Autowired 或
@Inject 。
In much the same way that Spring XML files are used as input when instantiating a ClassPathXmlApplicationContext,
you
can use @Configuration classes as input when instantiating an AnnotationConfigApplicationContext. This allows
for
completely XML-free usage of the Spring container, as the following example shows:
与在实例化 ClassPathXmlApplicationContext 时使用 Spring XML 文件作为输入的方式类似,你可以在实例化
AnnotationConfigApplicationContext 时使用 @Configuration 类作为输入。这允许完全无 XML 的使用 Spring 容器,如下例所示:
As mentioned earlier, AnnotationConfigApplicationContext is not limited to working only with @Configuration
classes.
Any @Component or JSR-330 annotated class may be supplied as input to the constructor, as the following example
shows:
如前所述, AnnotationConfigApplicationContext 不仅限于仅与 @Configuration 类一起工作。任何 @Component 或 JSR-330
注解的类都可以作为构造函数的输入,以下示例显示:
The preceding example assumes that MyServiceImpl, Dependency1, and Dependency2 use Spring dependency injection
annotations such as @Autowired.
前一个示例假设 MyServiceImpl 、 Dependency1 和 Dependency2 使用了 Spring 依赖注入注解,如 @Autowired 。
register(Class<?>…)
通过 register(Class<?>…) 编程构建容器
You can instantiate an AnnotationConfigApplicationContext by using a no-arg constructor and then configure it by
using
the register() method. This approach is particularly useful when programmatically building an
AnnotationConfigApplicationContext. The following example shows how to do so:
你可以通过使用无参构造函数来实例化 AnnotationConfigApplicationContext ,然后通过使用 register() 方法来配置它。这种方法在程序化构建
AnnotationConfigApplicationContext 时特别有用。以下示例展示了如何这样做:
scan(String…)启用组件扫描 scan(String…)
To enable component scanning, you can annotate your @Configuration class as follows:
要启用组件扫描,你可以按如下方式注解你的 @Configuration 类:
1
This annotation enables component scanning. 此注释启用组件扫描。
1
This annotation enables component scanning.
Experienced Spring users may be familiar with the XML declaration equivalent from Spring’s context: namespace,
shown
in the following example:
经验丰富的 Spring 用户可能熟悉 Spring 的 context: 命名空间中的 XML 声明等效项,如下例所示:
In the preceding example, the com.acme package is scanned to look for any @Component-annotated classes, and
those
classes are registered as Spring bean definitions within the container. AnnotationConfigApplicationContext exposes
the
scan(String…) method to allow for the same component-scanning functionality, as the following example shows:
在前面示例中, com.acme 包被扫描以查找任何 @Component -注释的类,这些类在容器内注册为 Spring bean 定义。
AnnotationConfigApplicationContext 提供了 scan(String…) 方法,以允许相同的组件扫描功能,如下例所示:
Remember that @Configuration classes are meta-annotated with @Component, so they are
candidates for component-scanning. In the preceding example, assuming that AppConfig is declared within the
com.acme
package (or any package underneath), it is picked up during the call to scan(). Upon refresh(), all its @Bean
methods are processed and registered as bean definitions within the container.
记住, @Configuration 类被元注解为 @Component ,因此它们是组件扫描的候选对象。在前面的示例中,假设 AppConfig 在
com.acme 包(或任何下级包)中声明,它将在调用 scan() 时被捕获。在 refresh() 之后,它的所有 @Bean 方法都会被处理并注册为容器内的
bean 定义。
AnnotationConfigWebApplicationContext
支持带有 AnnotationConfigWebApplicationContext 的 Web 应用程序
A WebApplicationContext variant of AnnotationConfigApplicationContext is available with
AnnotationConfigWebApplicationContext. You can use this implementation when configuring the Spring
ContextLoaderListener servlet listener, Spring MVC DispatcherServlet, and so forth. The following web.xml
snippet
configures a typical Spring MVC web application (note the use of the contextClass context-param and init-param):
A WebApplicationContext 变体与 AnnotationConfigApplicationContext 兼容。你可以在配置 Spring
ContextLoaderListener
Servlet 监听器、Spring MVC DispatcherServlet 等时使用此实现。以下 web.xml 段落配置了一个典型的 Spring MVC
网络应用程序(注意使用
contextClass context-param 和 init-param):
For programmatic use cases, a GenericWebApplicationContext can be used as an alternative to
AnnotationConfigWebApplicationContext. See the
GenericWebApplicationContext
javadoc for details.
对于程序化用例,可以使用 GenericWebApplicationContext 作为 AnnotationConfigWebApplicationContext 的替代。请参阅
GenericWebApplicationContext javadoc 获取详细信息。
@Bean 注解@Bean is a method-level annotation and a direct analog of the XML <bean/> element. The annotation supports some of
the attributes offered by <bean/>, such as:
You can use the @Bean annotation in a @Configuration-annotated or in a @Component-annotated class.
你可以在一个 @Configuration -注释或一个 @Component -注释的类中使用 @Bean 注释。
To declare a bean, you can annotate a method with the @Bean annotation. You use this method to register a bean
definition within an ApplicationContext of the type specified as the method’s return value. By default, the bean
name
is the same as the method name. The following example shows a @Bean method declaration:
声明一个 Bean,你可以使用 @Bean 注解方法。你使用此方法在指定为方法返回值的 ApplicationContext 中注册 Bean
定义。默认情况下,Bean 名称与方法名称相同。以下示例展示了 @Bean 方法声明:
The preceding configuration is exactly equivalent to the following Spring XML: 前述配置与以下 Spring XML 完全等价:
Both declarations make a bean named transferService available in the ApplicationContext, bound to an object
instance
of type TransferServiceImpl, as the following text image shows:
两个声明使名为 transferService 的 bean 在 ApplicationContext 中可用,绑定到类型为 TransferServiceImpl
的对象实例,如下文文本图像所示:
transferService -> com.acme.TransferServiceImpl
You can also use default methods to define beans. This allows composition of bean configurations by implementing interfaces with bean definitions on default methods.
You can also declare your @Bean method with an interface (or base class) return type, as the following example
shows:
你也可以使用接口(或基类)返回类型声明你的 @Bean 方法,如下例所示:
However, this limits the visibility for advance type prediction to the specified interface type (TransferService).
Then, with the full type (TransferServiceImpl) known to the container only once the affected singleton bean has
been
instantiated.
然而,这限制了高级类型预测的可见性,仅限于指定的接口类型( TransferService )。然后,在受影响的单例 bean
实例化后,容器才知道完整的类型(
TransferServiceImpl )。
Non-lazy singleton beans get instantiated according to their declaration order, so you may see different type
matching
results depending on when another component tries to match by a non-declared type (such as
@Autowired TransferServiceImpl, which resolves only once the transferService bean has been instantiated).
非懒加载的单例 bean 将根据其声明顺序进行实例化,因此你可能会看到不同的类型匹配结果,这取决于其他组件何时尝试通过未声明的类型进行匹配(例如
@Autowired TransferServiceImpl ,它仅在 transferService bean 实例化后才会解析)。
If you consistently refer to your types by a declared service interface, your @Bean return types may safely join
that
design decision.
如果你始终通过声明的服务接口来引用你的类型,你的 @Bean 返回类型可以安全地加入该设计决策。
However, for components that implement several interfaces or for components potentially referred to by their
implementation type, it is safer to declare the most specific return type possible (at least as specific as required
by
the injection points that refer to your bean).
然而,对于实现多个接口的组件或可能通过其实现类型引用的组件,声明最具体的返回类型更为安全(至少要具体到引用你的 bean
的注入点所要求的程度)。
A @Bean-annotated method can have an arbitrary number of parameters that describe the dependencies required to
build
that bean. For instance, if our TransferService requires an AccountRepository, we can materialize that
dependency
with a method parameter, as the following example shows:
一个 @Bean 注解的方法可以具有任意数量的参数,这些参数描述了构建该 bean 所需的依赖关系。例如,如果我们的
TransferService
需要 AccountRepository ,我们可以通过方法参数来实现这个依赖,如下例所示:
The resolution mechanism is pretty much identical to constructor-based dependency injection. See the relevant section for more details. 决议机制与基于构造函数的依赖注入几乎相同。请参阅相关部分以获取更多详细信息。
接收生命周期回调
Any classes defined with the @Bean annotation support the regular lifecycle callbacks and can use the
@PostConstruct
and @PreDestroy annotations from JSR-250. See JSR-250
annotations
for further details.
任何使用 @Bean 注解定义的类都支持常规的生命周期回调,并可以使用 JSR-250 中的 @PostConstruct 和 @PreDestroy
注解。有关详细信息,请参阅 JSR-250 注解。
The regular Spring lifecycle callbacks are fully supported as well. If a bean implements
InitializingBean, DisposableBean, or Lifecycle, their respective methods are called by the container.
Spring 的常规生命周期回调也得到了全面支持。如果一个 Bean 实现了 InitializingBean , DisposableBean 或 Lifecycle
,它们的相应方法将由容器调用。
The standard set of *Aware interfaces (such
as BeanFactoryAware, BeanNameAware,
MessageSourceAware, ApplicationContextAware,
and so on) are also fully supported.
标准集的 *Aware 接口(如 BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAware 等)也完全支持。
The @Bean annotation supports specifying arbitrary initialization and destruction callback methods, much like
Spring
XML’s init-method and destroy-method attributes on the bean element, as the following example shows:
The @Bean annotation supports specifying arbitrary initialization and destruction callback methods, much like
Spring
XML’s init-method and destroy-method attributes on the bean element, as the following example shows: @Bean
注解支持指定任意的初始化和销毁回调方法,类似于 Spring XML 中的 bean 元素上的 init-method 和 destroy-method
属性,如下例所示:
By default, beans defined with Java configuration that have a public close or shutdown method are automatically
enlisted with a destruction callback. If you have a public close or shutdown method and you do not wish for it
to be
called when the container shuts down, you can add @Bean(destroyMethod="") to your bean definition to disable the
default (inferred) mode.
默认情况下,使用 Java 配置定义的具有公共 close 或 shutdown 方法的 bean 会自动注册到销毁回调中。如果你有一个公共
close 或 shutdown 方法,并且不希望它在容器关闭时被调用,你可以在 bean 定义中添加 @Bean(destroyMethod="") 来禁用默认的
(inferred) 模式。
You may want to do that by default for a resource that you acquire with JNDI, as its lifecycle is managed outside
the
application. In particular, make sure to always do it for a DataSource, as it is known to be problematic on Java
EE
application servers.
你可能希望默认对通过 JNDI 获取的资源这样做,因为其生命周期由应用程序外部管理。特别是,务必始终对 DataSource 这样做,因为它在
Java EE 应用服务器上已知存在问题。
The following example shows how to prevent an automatic destruction callback for a DataSource:
以下示例展示了如何防止对 DataSource 的自动销毁回调
Also, with @Bean methods, you typically use programmatic JNDI lookups, either by using Spring’s JndiTemplate or
JndiLocatorDelegate helpers or straight JNDI InitialContext usage but not the JndiObjectFactoryBean variant
(which
would force you to declare the return type as the FactoryBean type instead of the actual target type, making it
harder
to use for cross-reference calls in other @Bean methods that intend to refer to the provided resource here).
此外,使用 @Bean 方法时,通常通过程序化 JNDI 查找,要么使用 Spring 的 JndiTemplate 或 JndiLocatorDelegate
助手,要么直接使用
JNDI InitialContext ,但不使用 JndiObjectFactoryBean 变体(这会迫使你将返回类型声明为 FactoryBean
类型,而不是实际的目标类型,使得在其他 @Bean 方法中用于引用此处提供的资源时更难以使用)。
In the case of BeanOne from the example above the preceding note, it would be equally valid to call the init()
method directly during construction, as the following example shows:
When you work directly in Java, you can do anything you like with your objects and do not always need to rely on the container lifecycle.
Spring includes the @Scope annotation so that you can specify the scope of a bean.
@Scope AnnotationYou can specify that your beans defined with the @Bean annotation should have a specific scope. You can use any of
the
standard scopes specified in the Bean Scopes section.
The default scope is singleton, but you can override this with the @Scope annotation, as the following example
shows:
@Scope andscoped-proxy@Scope 和scoped-proxySpring offers a convenient way of working with scoped dependencies
through scoped proxies. The easiest way to create such a proxy when using
the
XML configuration is the <aop:scoped-proxy/> element. Configuring your beans in Java with a @Scope annotation offers
equivalent support with the proxyMode attribute. The default is ScopedProxyMode.DEFAULT, which typically
indicates
that no scoped proxy should be created unless a different default has been configured at the component-scan
instruction
level. You can specify ScopedProxyMode.TARGET_CLASS, ScopedProxyMode.INTERFACES or ScopedProxyMode.NO.
Spring 提供了一种通过作用域代理处理作用域依赖的便捷方式。在 XML 配置中使用时,创建此类代理的最简单方法是使用
<aop:scoped-proxy/> 元素。使用 @Scope 注解在 Java 中配置你的 bean 提供了与 proxyMode 属性等效的支持。默认值为
ScopedProxyMode.DEFAULT ,通常表示除非在组件扫描指令级别配置了不同的默认值,否则不应创建作用域代理。你可以指定
ScopedProxyMode.TARGET_CLASS 、 ScopedProxyMode.INTERFACES 或 ScopedProxyMode.NO 。
If you port the scoped proxy example from the XML reference documentation (
see scoped proxies) to our @Bean using Java, it resembles the following:
如果你将 XML 参考文档中的作用域代理示例(见作用域代理)移植到我们的 @Bean ,使用 Java 实现时,它将类似于以下内容:
By default, configuration classes use a @Bean method’s name as the name of the resulting bean. This functionality
can
be overridden, however, with the name attribute, as the following example shows:
默认情况下,配置类使用 @Bean 方法名称作为结果 bean 的名称。然而,可以通过 name 属性覆盖此功能,如下例所示:
As discussed in Naming Beans, it is sometimes desirable to give a single bean multiple names,
otherwise known as bean aliasing. The name attribute of the @Bean annotation accepts a String array for this
purpose. The following example shows how to set a number of aliases for a bean:
如《命名豆子》中所述,有时需要给单个豆子赋予多个名称,这被称为豆别名。 @Bean 注解的 name
属性接受一个字符串数组用于此目的。以下示例展示了如何为一个豆子设置多个别名:
Sometimes, it is helpful to provide a more detailed textual description of a bean. This can be particularly useful when beans are exposed (perhaps through JMX) for monitoring purposes. 有时,提供更详细的文本描述一个豆子是有帮助的。当豆子被暴露出来(可能通过 JMX)用于监控目的时,这尤其有用。
To add a description to a @Bean, you can use the
@Description
annotation, as the following example shows:
要为 @Bean 添加描述,可以使用 @Description 注解,如下例所示:
@Configuration annotation1.12.4. 使用 @Configuration 注解
@Configuration is a class-level annotation indicating that an object is a source of bean definitions.
@Configuration
classes declare beans through @Bean-annotated methods. Calls to @Bean methods on @Configuration classes can
also
be used to define inter-bean dependencies. See Basic Concepts: @Bean and
@Configuration for a general introduction.
@Configuration 是一个类级别的注解,表示一个对象是 bean 定义的来源。 @Configuration 类通过 @Bean -注解的方法声明
bean。在 @Configuration 类上对 @Bean 方法的调用也可以用来定义 bean 之间的依赖关系。参见基本概念: @Bean 和
@Configuration 以获得一般介绍。
注入豆间依赖
When beans have dependencies on one another, expressing that dependency is as simple as having one bean method call another, as the following example shows: 当豆子之间存在依赖关系时,表达这种依赖关系就像一个豆子调用另一个豆子的方法一样简单,如下例所示:
In the preceding example, beanOne receives a reference to beanTwo through constructor injection.
在先前的示例中, beanOne 通过构造函数注入接收对 beanTwo 的引用。
This method of declaring inter-bean dependencies works only when the @Bean method is declared within a
@Configuration class. You cannot declare inter-bean dependencies by using plain @Component classes.
这种方法声明 bean 之间的依赖仅在 @Bean 方法在 @Configuration 类中声明时才有效。你不能通过使用纯 @Component 类来声明
bean 之间的依赖。
As noted earlier, lookup method injection is an advanced feature that you should use rarely. It is useful in cases where a singleton-scoped bean has a dependency on a prototype-scoped bean. Using Java for this type of configuration provides a natural means for implementing this pattern. 如前所述,查找方法注入是一个高级功能,你应该很少使用。在单例作用域的 bean 依赖于原型作用域的 bean 的情况下很有用。使用 Java 进行此类配置提供了一种实现此模式的自然方式。 The following example shows how to use lookup method injection: 以下示例展示了如何使用查找方法注入:
By using Java configuration, you can create a subclass of CommandManager where the abstract createCommand()
method
is overridden in such a way that it looks up a new (prototype) command object. The following example shows how to do
so:
通过使用 Java 配置,你可以创建一个 CommandManager 的子类,其中重写了抽象的 createCommand()
方法,使其查找一个新的(原型)命令对象。以下示例展示了如何实现:
Internally
关于 Java 基于配置内部工作原理的更多信息
Consider the following example, which shows a @Bean annotated method being called twice:
考虑以下示例,它展示了 @Bean 注释的方法被调用两次:
clientDao() has been called once in clientService1() and once in clientService2(). Since this method creates a
new
instance of ClientDaoImpl and returns it, you would normally expect to have two instances (one for each service).
That
definitely would be problematic: In Spring, instantiated beans have a singleton scope by default. This is where
the
magic comes in: All @Configuration classes are subclassed at startup-time with CGLIB. In the subclass, the child
method checks the container first for any cached (scoped) beans before it calls the parent method and creates a new
instance.
clientDao() 在 clientService1() 和 clientService2() 中被调用了一次。由于此方法创建了一个新的 ClientDaoImpl
实例并返回它,你通常会期望有两个实例(每个服务一个)。这肯定会有问题:在 Spring 中,默认情况下实例化的 bean 有一个
singleton
范围。这就是魔法所在:所有 @Configuration 类在启动时都会被 CGLIB
继承。在子类中,子方法在调用父方法并创建新实例之前,会首先检查容器中是否有任何缓存的(范围)bean。
The behavior could be different according to the scope of your bean. We are talking about singletons here. 根据你 bean 的作用域,行为可能会有所不同。我们这里讨论的是单例。
As of Spring 3.2, it is no longer necessary to add CGLIB to your classpath because CGLIB classes have been
repackaged
under org.springframework.cglib and included directly within the spring-core JAR.
截至 Spring 3.2 版本,不再需要在类路径中添加 CGLIB,因为 CGLIB 类已被重新打包为 org.springframework.cglib 并直接包含在
spring-core JAR 文件中。
There are a few restrictions due to the fact that CGLIB dynamically adds features at startup-time. In particular,
configuration classes must not be final. However, as of 4.3, any constructors are allowed on configuration classes,
including the use of @Autowired or a single non-default constructor declaration for default injection.
由于 CGLIB 在启动时动态添加功能,因此存在一些限制。特别是,配置类不能是最终的。然而,从 4.3 版本开始,配置类允许使用任何构造函数,包括使用
@Autowired 或单个非默认构造函数声明进行默认注入。
If you prefer to avoid any CGLIB-imposed limitations, consider declaring your @Bean methods on
non-@Configuration
classes (for example, on plain @Component classes instead). Cross-method calls between @Bean methods are not
then
intercepted, so you have to exclusively rely on dependency injection at the constructor or method level there.
如果你希望避免任何 CGLIB 强加的限制,请考虑在非 @Configuration 类上声明你的 @Bean 方法(例如,在普通 @Component
类上)。此时, @Bean 方法之间的跨方法调用不会被拦截,因此你必须在该处仅依赖于构造函数或方法级别的依赖注入。
1.12.5. 基于 Java 的配置编写
Spring’s Java-based configuration feature lets you compose annotations, which can reduce the complexity of your configuration. Spring 的基于 Java 的配置功能让你可以组合注解,这可以减少你配置的复杂性。
@Import Annotation 使用@Import 注解Much as the <import/> element is used within Spring XML files to aid in modularizing configurations, the @Import
annotation allows for loading @Bean definitions from another configuration class, as the following example shows:
尽管 <import/> 元素在 Spring XML 文件中用于帮助模块化配置,但 @Import 注解允许从另一个配置类加载 @Bean 定义,如下例所示:
Now, rather than needing to specify both ConfigA.class and ConfigB.class when instantiating the context, only
ConfigB needs to be supplied explicitly, as the following example shows:
现在,在实例化上下文时,不再需要指定 ConfigA.class 和 ConfigB.class ,只需明确提供 ConfigB ,如下例所示:
This approach simplifies container instantiation, as only one class needs to be dealt with, rather than requiring
you to
remember a potentially large number of @Configuration classes during construction.
这种方法简化了容器实例化,因为只需要处理一个类,而不是在构建过程中记住可能的大量 @Configuration 类。
As of Spring Framework 4.2, @Import also supports references to regular component classes, analogous to the
AnnotationConfigApplicationContext.register method. This is particularly useful if you want to avoid component
scanning, by using a few configuration classes as entry points to explicitly define all your components.
截至 Spring Framework 4.2 版本, @Import 也支持对常规组件类的引用,类似于
AnnotationConfigApplicationContext.register
方法。如果你想通过使用少量配置类作为入口点来显式定义所有组件,以避免组件扫描,这将特别有用。
@Bean Definitions注入导入的 @Bean 定义的依赖项
The preceding example works but is simplistic. In most practical scenarios, beans have dependencies on one another
across configuration classes. When using XML, this is not an issue, because no compiler is involved, and you can
declare
ref="someBean" and trust Spring to work it out during container initialization. When using @Configuration
classes,
the Java compiler places constraints on the configuration model, in that references to other beans must be valid
Java
syntax.
前一个例子可行但过于简单。在大多数实际场景中,配置类之间的豆子相互依赖。当使用 XML 时,这不是问题,因为没有编译器参与,你可以声明
ref="someBean" 并信任 Spring 在容器初始化期间解决它。当使用 @Configuration 类时,Java 编译器对配置模型施加约束,即对其他豆子的引用必须是有效的
Java 语法。
Fortunately, solving this problem is simple. As we already discussed, a @Bean method
can
have an arbitrary number of parameters that describe the bean dependencies. Consider the following more real-world
scenario with several @Configuration classes, each depending on beans declared in the others:
幸运的是,解决这个问题很简单。正如我们之前讨论的,一个 @Bean 方法可以有任意数量的参数来描述 bean
的依赖关系。考虑以下更贴近现实场景的例子,其中包含几个 @Configuration 类,每个类都依赖于在其他类中声明的 bean:
There is another way to achieve the same result. Remember that @Configuration classes are ultimately only another
bean
in the container: This means that they can take advantage of @Autowired and @Value injection and other features
the
same as any other bean.
有另一种实现相同结果的方法。记住, @Configuration 类最终在容器中只是另一个 bean:这意味着它们可以像任何其他 bean 一样利用
@Autowired 和 @Value 注入以及其他功能。
Make sure that the dependencies you inject that way are of the simplest kind only. @Configuration classes are
processed quite early during the initialization of the context, and forcing a dependency to be injected this way may
lead to unexpected early initialization. Whenever possible, resort to parameter-based injection, as in the preceding
example.
确保以这种方式注入的依赖项仅限于最简单的类型。 @Configuration
类在上下文初始化过程中处理得相当早,强制以这种方式注入依赖可能导致意外的早期初始化。尽可能使用参数化注入,如前例所示。
Avoid access to locally defined beans within a @PostConstruct method on the same configuration class. This
effectively
leads to a circular reference since non-static @Bean methods semantically require a fully initialized
configuration
class instance to be called on. With circular references disallowed (e.g. in Spring Boot 2.6+), this may trigger a
BeanCurrentlyInCreationException.
避免在同一个配置类上的 @PostConstruct 方法中访问本地定义的 bean。这实际上会导致循环引用,因为非静态 @Bean
方法在语义上需要调用一个完全初始化的配置类实例。由于循环引用被禁止(例如在 Spring Boot 2.6+中),这可能会触发
BeanCurrentlyInCreationException 。
Also, be particularly careful with BeanPostProcessor and BeanFactoryPostProcessor definitions through @Bean.
Those
should usually be declared as static @Bean methods, not triggering the instantiation of their containing
configuration
class. Otherwise, @Autowired and @Value may not work on the configuration class itself, since it is possible to
create it as a bean instance earlier than
AutowiredAnnotationBeanPostProcessor.
此外,特别小心通过 @Bean 对 BeanPostProcessor 和 BeanFactoryPostProcessor 的定义。这些通常应声明为 static @Bean
方法,而不是触发其包含的配置类的实例化。否则, @Autowired 和 @Value 可能无法在配置类本身上工作,因为有可能在
AutowiredAnnotationBeanPostProcessor 之前将其创建为 bean 实例。
The following example shows how one bean can be autowired to another bean: 以下示例展示了如何将一个 Bean 自动装配到另一个 Bean 中:
Constructor injection in @Configuration classes is only supported as of Spring Framework 4.3. Note also that there
is
no need to specify @Autowired if the target bean defines only one constructor.
构造函数注入在 @Configuration 类中仅从 Spring Framework 4.3 版本开始支持。注意,如果目标 bean 只定义了一个构造函数,则无需指定
@Autowired 。
Fully-qualifying imported beans for ease of navigation 完全限定导入的豆类以方便导航
In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining
exactly
where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking
at
ServiceConfig, how do you know exactly where the @Autowired AccountRepository bean is declared? It is not
explicit
in the code, and this may be just fine. Remember that the Spring Tools for Eclipse
provides
tooling that can render graphs showing how everything is wired, which may be all you need. Also, your Java IDE can
easily find all declarations and uses of the AccountRepository type and quickly show you the location of @Bean
methods that return that type.
在先前的场景中,使用 @Autowired 效果良好,并提供了所需的模块化,但确定自动装配的 bean 定义的确切声明位置仍然有些模糊。例如,作为一个查看
ServiceConfig 的开发者,你如何确切知道 @Autowired AccountRepository bean 是在哪里声明的?这在代码中并不明确,这可能完全没问题。记住,Spring
Tools for Eclipse 提供了可以显示如何连接所有组件的图形工具,这可能就是你所需要的。此外,你的 Java IDE 可以轻松找到
AccountRepository 类型的所有声明和使用,并快速显示返回该类型的方法的位置。
In cases where this ambiguity is not acceptable and you wish to have direct navigation from within your IDE from one
@Configuration class to another, consider autowiring the configuration classes themselves. The following example
shows
how to do so:
在无法接受这种歧义且你希望从你的 IDE 中直接导航从一个 @Configuration 类到另一个类的情况下,请考虑自动装配配置类本身。以下示例展示了如何操作:
In the preceding situation, where AccountRepository is defined is completely explicit. However, ServiceConfig is
now
tightly coupled to RepositoryConfig. That is the tradeoff. This tight coupling can be somewhat mitigated by using
interface-based or abstract class-based @Configuration classes. Consider the following example:
在前面的情况下, AccountRepository 的定义是完全明确的。然而,现在 ServiceConfig 与 RepositoryConfig
紧密耦合。这就是权衡。这种紧密耦合可以通过使用基于接口或抽象类的 @Configuration 类来在一定程度上缓解。考虑以下示例:
Now ServiceConfig is loosely coupled with respect to the concrete DefaultRepositoryConfig, and built-in IDE
tooling
is still useful: You can easily get a type hierarchy of RepositoryConfig implementations. In this way, navigating
@Configuration classes and their dependencies becomes no different than the usual process of navigating
interface-based code.
现在 ServiceConfig 与具体的 DefaultRepositoryConfig 松散耦合,内置 IDE 工具仍然有用:你可以轻松获取
RepositoryConfig
实现的类型层次结构。这样,导航 @Configuration 类及其依赖关系与基于接口的代码的常规导航过程没有区别。
If you want to influence the startup creation order of certain beans, consider declaring some of them as @Lazy
(for
creation on first access instead of on startup) or as @DependsOn certain other beans (making sure that specific
other
beans are created before the current bean, beyond what the latter’s direct dependencies imply).
如果你想影响某些豆子的启动创建顺序,请考虑将其中一些声明为 @Lazy (在首次访问时创建,而不是在启动时)或作为
@DependsOn
某些其他豆子(确保在当前豆子之前创建特定的其他豆子,而不仅仅是后者的直接依赖所暗示的)。
@Configuration Classes or@Bean Methods条件包含 @Configuration 类或 @Bean 方法
It is often useful to conditionally enable or disable a complete @Configuration class or even individual @Bean
methods, based on some arbitrary system state. One common example of this is to use the @Profile annotation to
activate beans only when a specific profile has been enabled in the Spring Environment (
see Bean Definition Profiles for details).
通常,根据某些任意系统状态,有条件地启用或禁用整个 @Configuration 类或甚至单个 @Bean 方法是有用的。一个常见的例子是使用
@Profile 注解,仅在 Spring Environment (有关详细信息,请参阅 Bean 定义配置文件)中启用特定配置文件时激活 bean。
The @Profile annotation is actually implemented by using a much more flexible annotation called
@Conditional.
The @Conditional annotation indicates specific org.springframework.context.annotation.Condition implementations
that
should be consulted before a @Bean is registered.
@Profile 注解实际上是通过使用一个更灵活的注解 @Conditional 来实现的。 @Conditional 注解表示在注册 @Bean
之前应查阅的特定
org.springframework.context.annotation.Condition 实现。
Implementations of the Condition interface provide a matches(…) method that returns true or false. For
example,
the following listing shows the actual Condition implementation used for @Profile:
实现 Condition 接口的代码提供了一个返回 true 或 false 的 matches(…) 方法。例如,以下列表显示了用于 @Profile
的实际
Condition 实现:
See the
@Conditional
javadoc for more detail.
查看 @Conditional javadoc 获取更多详细信息。
结合 Java 和 XML 配置
Spring’s @Configuration class support does not aim to be a 100% complete replacement for Spring XML. Some
facilities,
such as Spring XML namespaces, remain an ideal way to configure the container.
Spring 的 @Configuration 类支持并不旨在完全替代 Spring XML。一些功能,如 Spring XML 命名空间,仍然是配置容器的理想方式。
In cases where XML is convenient or necessary, you have a choice: either instantiate the container in an
“XML-centric”
way by using, for example, ClassPathXmlApplicationContext, or instantiate it in a “Java-centric” way by using
AnnotationConfigApplicationContext and the @ImportResource annotation to import XML as needed.
在 XML 方便或必要的情况下,你有选择:要么通过使用例如 ClassPathXmlApplicationContext 以“XML 中心”的方式实例化容器,要么通过使用
AnnotationConfigApplicationContext 和 @ImportResource 注解以按需导入 XML 的方式以“Java 中心”的方式实例化它。
@Configuration Classes以 XML 为中心的 @Configuration 类使用
It may be preferable to bootstrap the Spring container from XML and include @Configuration classes in an ad-hoc
fashion. For example, in a large existing codebase that uses Spring XML, it is easier to create @Configuration
classes
on an as-needed basis and include them from the existing XML files. Later in this section, we cover the options for
using @Configuration classes in this kind of “XML-centric” situation.
可能更倾向于从 XML 启动 Spring 容器并以临时方式包含 @Configuration 类。例如,在一个使用 Spring XML 的大型现有代码库中,按需创建
@Configuration 类并从现有 XML 文件中包含它们更容易。在本节稍后,我们将介绍在这种“以 XML 为中心”的情况下使用
@Configuration 类的选项。
Declaring @Configuration classes as plain Spring <bean/> elements
声明 @Configuration 类为纯 Spring <bean/> 元素
Remember that @Configuration classes are ultimately bean definitions in the container. In this series examples, we
create a @Configuration class named AppConfig and include it within system-test-config.xml as a <bean/>
definition. Because <context:annotation-config/> is switched on, the container recognizes the @Configuration
annotation and processes the @Bean methods declared in AppConfig properly.
记住, @Configuration 类在容器中最终是 bean 定义。在这个系列示例中,我们创建一个名为 AppConfig 的 @Configuration
类,并将其作为 <bean/> 定义包含在 system-test-config.xml 中。因为 <context:annotation-config/> 已开启,容器能够识别
@Configuration 注解并正确处理 AppConfig 中声明的 @Bean 方法。
The following example shows an ordinary configuration class in Java: 以下示例展示了 Java 中的一个普通配置类:
The following example shows part of a sample system-test-config.xml file:
以下示例展示了样本 system-test-config.xml 文件的一部分:
The following example shows a possible jdbc.properties file:
以下示例显示了一个可能的 jdbc.properties 文件:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa jdbc.password=
In system-test-config.xml file, the AppConfig <bean/> does not declare an id element. While it would be
acceptable to do so, it is unnecessary, given that no other bean ever refers to it, and it is unlikely to be
explicitly
fetched from the container by name. Similarly, the DataSource bean is only ever autowired by type, so an explicit
bean
id is not strictly required.
在 system-test-config.xml 文件中, AppConfig <bean/> 没有声明一个 id 元素。虽然这样做是可以接受的,但由于没有其他
bean 引用它,并且不太可能通过名称显式地从容器中获取它,因此这样做是不必要的。同样, DataSource bean
也仅通过类型自动装配,因此显式地声明一个
bean id 并不是严格必要的。
Using context:component-scan/ to pick up @Configuration classes
使用 `` 检索 @Configuration 类
Because @Configuration is meta-annotated with @Component, @Configuration-annotated classes are automatically
candidates for component scanning. Using the same scenario as described in the previous example, we can redefine
system-test-config.xml to take advantage of component-scanning. Note that, in this case, we need not explicitly
declare <context:annotation-config/>, because <context:component-scan/> enables the same functionality.
因为 @Configuration 被元注解为 @Component ,所以 @Configuration 注解的类自动成为组件扫描的候选。使用前一个示例中描述的相同场景,我们可以重新定义
system-test-config.xml 以利用组件扫描。注意,在这种情况下,我们不需要显式声明 <context:annotation-config/> ,因为
<context:component-scan/> 启用了相同的功能。
The following example shows the modified system-test-config.xml file:
以下示例显示了修改后的 system-test-config.xml 文件:
@Configuration Class-centric Use of XML with@ImportResource@Configuration 以类为中心的 XML 使用 @ImportResource
In applications where @Configuration classes are the primary mechanism for configuring the container, it is still
likely necessary to use at least some XML. In these scenarios, you can use @ImportResource and define only as much
XML
as you need. Doing so achieves a “Java-centric” approach to configuring the container and keeps XML to a bare
minimum.
The following example (which includes a configuration class, an XML file that defines a bean, a properties file, and
the
main class) shows how to use the @ImportResource annotation to achieve “Java-centric” configuration that uses
XML as
needed:
在以 @Configuration 类为主要配置容器的应用中,仍然可能需要使用至少一些 XML。在这些场景中,你可以使用
@ImportResource
并仅定义所需的 XML 量。这样做可以实现“以 Java 为中心”的容器配置方法,并将 XML 量保持在最低限度。以下示例(包括配置类、定义
bean 的 XML 文件、属性文件和 main 类)展示了如何使用 @ImportResource 注解实现“以 Java 为中心”的配置,该配置根据需要使用
XML:
jdbc.properties jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa jdbc.password=