Composing Java-based Configurations

Spring 基于 Java 的配置功能让您可以组合注解,这可以降低配置的复杂性。

Spring’s Java-based configuration feature lets you compose annotations, which can reduce the complexity of your configuration.

Using the @Import Annotation

<import/> 元素用于 Spring XML 文件中,以帮助模块化配置,@Import 注解允许从另一个配置类加载 @Bean 定义,如下例所示:

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:

  • Java

  • Kotlin

@Configuration
public class ConfigA {

	@Bean
	public A a() {
		return new A();
	}
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

	@Bean
	public B b() {
		return new B();
	}
}
@Configuration
class ConfigA {

	@Bean
	fun a() = A()
}

@Configuration
@Import(ConfigA::class)
class ConfigB {

	@Bean
	fun b() = B()
}

与在实例化上下文时需要指定 ConfigA.classConfigB.class 不同,现在只需要显式提供 ConfigB,如下例所示:

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:

  • Java

  • Kotlin

public static void main(String[] args) {
	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);
}
import org.springframework.beans.factory.getBean

fun main() {
	val ctx = AnnotationConfigApplicationContext(ConfigB::class.java)

	// now both beans A and B will be available...
	val a = ctx.getBean<A>()
	val b = ctx.getBean<B>()
}

这种方法简化了容器实例化,因为只需处理一个类,而不是在构建期间要求记住大量的 @Configuration 类。

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.

从 Spring Framework 4.2 起,@Import 还支持对常规组件类的引用,类似于 AnnotationConfigApplicationContext.register 方法。如果你想避免组件扫描,使用一些配置类作为入口点来明确定义你的所有组件,这会特别有用。

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.

Injecting Dependencies on Imported @Bean Definitions

前面的示例可用,但过于简单。在大多数实际场景中,bean 依赖于跨配置类的其他 bean。在使用 XML 时,这不是问题,因为它不涉及编译器,并且可以声明 ref="someBean",并相信 Spring 会在容器初始化期间解决它。在使用 @Configuration 类时,Java 编译器对配置模型施加了约束,因为对其他 bean 的引用必须是有效的 Java 语法。

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.

幸运的是,解决这个问题很简单。如 we already discussed@Bean 方法可以具有任意数量的参数来描述 bean 依赖项。考虑以下更现实的场景,其中包含多个 @Configuration 类,每个类都依赖于在其他类中声明的 bean:

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:

  • Java

  • Kotlin

@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");
}
import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

	@Bean
	fun transferService(accountRepository: AccountRepository): TransferService {
		return TransferServiceImpl(accountRepository)
	}
}

@Configuration
class RepositoryConfig {

	@Bean
	fun accountRepository(dataSource: DataSource): AccountRepository {
		return JdbcAccountRepository(dataSource)
	}
}

@Configuration
@Import(ServiceConfig::class, RepositoryConfig::class)
class SystemTestConfig {

	@Bean
	fun dataSource(): DataSource {
		// return new DataSource
	}
}


fun main() {
	val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
	// everything wires up across configuration classes...
	val transferService = ctx.getBean<TransferService>()
	transferService.transfer(100.00, "A123", "C456")
}

还有另一种方法可以实现相同的结果。记住,@Configuration 类最终只是容器中的另一个 bean:这意味着它们可以利用 @Autowired@Value 注入以及与任何其他 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 类在上下文的初始化期间很早就被处理,强制一个依赖项以这种方式被注入可能会导致意外的早期初始化。在可能的情况下,使用基于参数的注入,如前面的示例。

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.

避免在同一个配置类的 @PostConstruct 方法中访问本地定义的 bean。这实际上会导致循环引用,因为非静态 @Bean 方法在语义上要求调用一个完全初始化的配置类实例。在禁止循环引用(例如,在 Spring Boot 2.6+ 中)时,这可能会触发 BeanCurrentlyInCreationException

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.

此外,要特别小心通过 @Bean 定义的 BeanPostProcessorBeanFactoryPostProcessor。这些通常应声明为 static @Bean 方法,而不是触发其中一个配置类的实例化。否则, @Autowired@Value 可能无法在配置类本身上工作,因为有可能比 AutowiredAnnotationBeanPostProcessor 更早地创建它作为 Bean 实例。

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 自动装配到另一个 bean:

The following example shows how one bean can be autowired to another bean:

  • Java

  • Kotlin

@Configuration
public class ServiceConfig {

	@Autowired
	private AccountRepository accountRepository;

	@Bean
	public TransferService transferService() {
		return new TransferServiceImpl(accountRepository);
	}
}

@Configuration
public class RepositoryConfig {

	private final DataSource dataSource;

	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");
}
import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

	@Autowired
	lateinit var accountRepository: AccountRepository

	@Bean
	fun transferService(): TransferService {
		return TransferServiceImpl(accountRepository)
	}
}

@Configuration
class RepositoryConfig(private val dataSource: DataSource) {

	@Bean
	fun accountRepository(): AccountRepository {
		return JdbcAccountRepository(dataSource)
	}
}

@Configuration
@Import(ServiceConfig::class, RepositoryConfig::class)
class SystemTestConfig {

	@Bean
	fun dataSource(): DataSource {
		// return new DataSource
	}
}

fun main() {
	val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
	// everything wires up across configuration classes...
	val transferService = ctx.getBean<TransferService>()
	transferService.transfer(100.00, "A123", "C456")
}

@Configuration 类中的构造函数注入从 Spring Framework 4.3 起才受支持。另外请注意,如果目标 bean 仅定义了一个构造函数,则不需要指定 @Autowired

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.

[id="beans-java-injecting-imported-beans-fq"]Fully-qualifying imported beans for ease of navigation

在上述场景中,使用 @Autowired 效果很好,并提供了所需的模块化,但确定自动装配 Bean 定义的确切声明位置仍然有些含糊。例如,作为查看 ServiceConfig 的开发人员,您如何确切知道 @Autowired AccountRepository Bean 的声明位置?这在代码中并没有明确说明,这可能很好。请记住, Spring Tools for Eclipse 提供了可以呈现图表以显示如何装配所有内容的工具,这可能就是您所需要的。此外,您的 Java IDE 可以轻松找到 AccountRepository 类型的全部声明和用法,并快速向您展示返回该类型的 @Bean 方法的位置。

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.

在无法接受此模棱两可的情况下,并且希望从一个 @Configuration 类直接导航到另一个 @Configuration 类,请考虑自动装配配置类本身。以下示例展示了如何执行此操作:

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:

  • Java

  • Kotlin

@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());
	}
}
@Configuration
class ServiceConfig {

	@Autowired
	private lateinit var repositoryConfig: RepositoryConfig

	@Bean
	fun transferService(): TransferService {
		// navigate 'through' the config class to the @Bean method!
		return TransferServiceImpl(repositoryConfig.accountRepository())
	}
}

在前面的情况下,AccountRepository 的定义是完全明确的。但是,ServiceConfig 现在与 RepositoryConfig 紧密耦合。这就是权衡。这种紧密耦合可以通过使用基于接口或基于抽象类的 @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:

  • Java

  • Kotlin

@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");
}
import org.springframework.beans.factory.getBean

@Configuration
class ServiceConfig {

	@Autowired
	private lateinit var repositoryConfig: RepositoryConfig

	@Bean
	fun transferService(): TransferService {
		return TransferServiceImpl(repositoryConfig.accountRepository())
	}
}

@Configuration
interface RepositoryConfig {

	@Bean
	fun accountRepository(): AccountRepository
}

@Configuration
class DefaultRepositoryConfig : RepositoryConfig {

	@Bean
	fun accountRepository(): AccountRepository {
		return JdbcAccountRepository(...)
	}
}

@Configuration
@Import(ServiceConfig::class, DefaultRepositoryConfig::class)  // import the concrete config!
class SystemTestConfig {

	@Bean
	fun dataSource(): DataSource {
		// return DataSource
	}

}

fun main() {
	val ctx = AnnotationConfigApplicationContext(SystemTestConfig::class.java)
	val transferService = ctx.getBean<TransferService>()
	transferService.transfer(100.00, "A123", "C456")
}

现在 ServiceConfig 对于具体的 DefaultRepositoryConfig 是松散耦合的,并且内置的 IDE 工具仍然有用:您可以轻松地获得 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.

Influencing the Startup of @Bean-defined Singletons

如果希望影响某些单例 bean 的启动创建顺序,请考虑将其中一些 bean 声明为 @Lazy,以便在首次访问时创建,而不是在启动时创建。

If you want to influence the startup creation order of certain singleton beans, consider declaring some of them as @Lazy for creation on first access instead of on startup.

@DependsOn 强制某些其他 bean 首先初始化,从而确保在当前 bean 之前创建指定的 bean,超越后者的直接依赖项所暗示的内容。

@DependsOn forces certain other beans to be initialized first, making sure that the specified beans are created before the current bean, beyond what the latter’s direct dependencies imply.

Background Initialization

从 6.2 开始,有一个后台初始化选项:@Bean(bootstrap=BACKGROUND) 允许为后台初始化选择特定 bean,涵盖上下文中启动的每个此类 bean 的整个 bean 创建步骤。

As of 6.2, there is a background initialization option: @Bean(bootstrap=BACKGROUND) allows for singling out specific beans for background initialization, covering the entire bean creation step for each such bean on context startup.

具有非惰性注入点的依赖 bean 会自动等待 bean 实例完成。将在上下文启动结束时强制完成所有常规后台初始化。只有另外标记为 @Lazy 的 bean 可以稍后完成(直到首次实际访问)。

Dependent beans with non-lazy injection points automatically wait for the bean instance to be completed. All regular background initializations are forced to complete at the end of context startup. Only beans additionally marked as @Lazy are allowed to be completed later (up until the first actual access).

后台初始化通常与依赖 bean 中的 @Lazy(或 ObjectProvider)注入点一起使用。否则,当需要早期注入实际的后台初始化 bean 实例时,主引导线程将被阻塞。

Background initialization typically goes together with @Lazy (or ObjectProvider) injection points in dependent beans. Otherwise, the main bootstrap thread is going to block when an actual background-initialized bean instance needs to be injected early.

这种形式的并发启动适用于单个 bean:如果这样的 bean 依赖于其他 bean,则需要已经初始化它们,要么通过简单地通过 @DependsOn 提前声明,要么通过 @DependsOn,在为受影响的 bean 触发后台初始化之前强制在主引导线程中初始化。

This form of concurrent startup applies to individual beans: if such a bean depends on other beans, they need to have been initialized already, either simply through being declared earlier or through @DependsOn which enforces initialization in the main bootstrap thread before background initialization for the affected bean is triggered.

如果想要真正启动后台引导,就必须声明一个类型为 ExecutorbootstrapExecutor bean。否则,将在运行时忽略后台标记。

A bootstrapExecutor bean of type Executor must be declared for background bootstrapping to be actually active. Otherwise, the background markers will be ignored at runtime.

引导执行器可以仅仅用于启动目的的受限执行器,也可以是为其他目的提供服务的共享线程池。

The bootstrap executor may be a bounded executor just for startup purposes or a shared thread pool which serves for other purposes as well.

Conditionally Include @Configuration Classes or @Bean Methods

通常很方便根据某些任意的系统状态对一个完整的 @Configuration 类甚至对 @Bean 方法有条件地启用或禁用它们。一个常见的示例是使用 @Profile 注释,在仅在 Spring Environment 中启用了特定配置文件时激活 bean(有关详细信息,请参见 Bean Definition Profiles)。

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).

@Profile 注解实际上是通过使用更灵活的称为 @Conditional 的注释实现的。@Conditional 注解指示在注册 @Bean 之前应咨询的特定 org.springframework.context.annotation.Condition 实现。

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.

Condition 接口的实现提供了一个返回 truefalsematches(…​) 方法。例如,以下清单显示了用于 @Profile 的实际 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:

  • Java

  • Kotlin

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
	// Read the @Profile annotation attributes
	MultiValueMap<String, Object> 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;
}
override fun matches(context: ConditionContext, metadata: AnnotatedTypeMetadata): Boolean {
	// Read the @Profile annotation attributes
	val attrs = metadata.getAllAnnotationAttributes(Profile::class.java.name)
	if (attrs != null) {
		for (value in attrs["value"]!!) {
			if (context.environment.acceptsProfiles(Profiles.of(*value as Array<String>))) {
				return true
			}
		}
		return false
	}
	return true
}

有关更多详细信息,请参阅 @Conditional javadoc。

See the @Conditional javadoc for more detail.

Combining Java and XML Configuration

Spring 的 @Configuration 类支持不旨在成为 Spring XML 的 100% 的完整替代品。Spring XML 命名空间等某些功能仍然是配置容器的理想途径。在 XML 方便或必要的情况下,您有两种选择:通过使用 ClassPathXmlApplicationContext 等方式以“以 XML 为中心”的方式实例化容器,或者通过使用 AnnotationConfigApplicationContext@ImportResource 注解以“以 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. 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-centric Use of @Configuration Classes

最好从 XML 中引导 Spring 容器,并以临时的方式包含 @Configuration 类。例如,在一个使用 Spring XML 的大型现有代码库中,按需创建 @Configuration 类并从现有的 XML 文件中包含它们更为容易。在本节的后面,我们将介绍在这种“以 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.

[id="beans-java-combining-xml-centric-declare-as-bean"]Declaring @Configuration classes as plain Spring <bean/> elements

请记住,@Configuration 类最终是容器中的 bean 定义。在此系列示例中,我们创建名为 AppConfig@Configuration 类,并将其作为 <bean/> 定义包含在 system-test-config.xml 中。由于启用了 <context:annotation-config/>,因此容器会识别 @Configuration 注解,并正确处理 AppConfig 中声明的 @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.

以下示例显示了 Java 中的一个普通配置类:

The following example shows an ordinary configuration class in Java:

  • Java

  • Kotlin

@Configuration
public class AppConfig {

	@Autowired
	private DataSource dataSource;

	@Bean
	public AccountRepository accountRepository() {
		return new JdbcAccountRepository(dataSource);
	}

	@Bean
	public TransferService transferService() {
		return new TransferService(accountRepository());
	}
}
@Configuration
class AppConfig {

	@Autowired
	private lateinit var dataSource: DataSource

	@Bean
	fun accountRepository(): AccountRepository {
		return JdbcAccountRepository(dataSource)
	}

	@Bean
	fun transferService() = TransferService(accountRepository())
}

以下示例显示了示例 system-test-config.xml 文件的一部分:

The following example shows part of a sample system-test-config.xml file:

<beans>
	<!-- enable processing of annotations such as @Autowired and @Configuration -->
	<context:annotation-config/>
	<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

	<bean class="com.acme.AppConfig"/>

	<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="url" value="${jdbc.url}"/>
		<property name="username" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
</beans>

以下示例显示了一个可能的 jdbc.properties 文件:

The following example shows a possible jdbc.properties file:

jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa jdbc.password=

  • Java

  • Kotlin

public static void main(String[] args) {
	ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
	TransferService transferService = ctx.getBean(TransferService.class);
	// ...
}
fun main() {
	val ctx = ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml")
	val transferService = ctx.getBean<TransferService>()
	// ...
}

system-test-config.xml 文件中,AppConfig <bean/> 没有声明 id 元素。虽然可以这样做,但没有必要,因为没有任何其他 bean 引用它,并且不可能通过名称从容器明确获取它。类似地,DataSource bean 总是仅按类型自动装配,因此一个明确的 bean id 不是严格必需的。

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.

[id="beans-java-combining-xml-centric-component-scan"] Using <context:component-scan/> to pick up @Configuration classes

由于 @Configuration 已使用 @Component 进行元注解,因此带 @Configuration 注释的类自动成为组件扫描的候选对象。使用与上一个示例中描述的相同场景,我们可以重新定义 system-test-config.xml 以利用组件扫描。请注意,在这种情况下,我们不需要显式声明 <context:annotation-config/>,因为 <context:component-scan/> 启用了相同的功能。

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.

以下示例显示了修改后的 system-test-config.xml 文件:

The following example shows the modified system-test-config.xml file:

<beans>
	<!-- picks up and registers AppConfig as a bean definition -->
	<context:component-scan base-package="com.acme"/>
	<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

	<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="url" value="${jdbc.url}"/>
		<property name="username" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
</beans>

@Configuration Class-centric Use of XML with @ImportResource

@Configuration 类为配置容器的主要机制的应用程序中,仍然可能需要至少使用一些 XML。在这些场景中,您可以使用 @ImportResource,并且仅定义所需数量的 XML。这样做实现了对容器进行“以 Java 为中心”的配置的方法,并将 XML 保持在最低限度。以下示例(包括配置类、定义 bean 的 XML 文件、属性文件和 main 类)展示了如何使用 @ImportResource 注解来实现所需的“以 Java 为中心”的 XML 配置:

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:

  • Java

  • Kotlin

@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);
	}
}
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
class AppConfig {

	@Value("\${jdbc.url}")
	private lateinit var url: String

	@Value("\${jdbc.username}")
	private lateinit var username: String

	@Value("\${jdbc.password}")
	private lateinit var password: String

	@Bean
	fun dataSource(): DataSource {
		return DriverManagerDataSource(url, username, password)
	}
}
properties-config.xml
<beans>
	<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>

jdbc.properties jdbc.url=jdbc:hsqldb:hsql://localhost/xdb jdbc.username=sa jdbc.password=

  • Java

  • Kotlin

public static void main(String[] args) {
	ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
	TransferService transferService = ctx.getBean(TransferService.class);
	// ...
}
import org.springframework.beans.factory.getBean

fun main() {
	val ctx = AnnotationConfigApplicationContext(AppConfig::class.java)
	val transferService = ctx.getBean<TransferService>()
	// ...
}