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.class
和 ConfigB.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 起, |
As of Spring Framework 4.2, |
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
定义的 BeanPostProcessor
和 BeanFactoryPostProcessor
。这些通常应声明为 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")
}
|
Constructor injection in |
在上述场景中,使用 @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.
如果想要真正启动后台引导,就必须声明一个类型为 A 引导执行器可以仅仅用于启动目的的受限执行器,也可以是为其他目的提供服务的共享线程池。 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
接口的实现提供了一个返回 true
或 false
的 matches(…)
方法。例如,以下清单显示了用于 @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.
@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>()
// ...
}
在 |
In |
@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>()
// ...
}