此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0spring-doc.cadn.net.cn

环境抽象

Environment接口 是集成在容器中的抽象,它对两个键进行建模 应用程序环境的各个方面:配置文件属性spring-doc.cadn.net.cn

配置文件是要注册到 容器。可以将 Bean 分配给配置文件 无论是在 XML 中定义还是使用注释定义。的作用Environmentobject 替换为 relation to profiles 用于确定当前处于活动状态的用户档案(如果有), 以及默认情况下应处于活动状态的配置文件(如果有)。spring-doc.cadn.net.cn

属性在几乎所有应用程序中都起着重要作用,可能源自 多种来源:属性文件、JVM 系统属性、系统环境 变量, JNDI, servlet 上下文参数, 临时Properties对象Map对象,等等 上。的作用Environmentobject 的 具有便捷服务界面的用户,用于配置属性源和解析 属性。spring-doc.cadn.net.cn

Bean 定义配置文件

Bean 定义配置文件在核心容器中提供了一种机制,该机制允许 在不同环境中注册不同的 bean。“环境”这个词 对不同的用户可能意味着不同的事情,而此功能可以帮助解决许多问题 使用案例,包括:spring-doc.cadn.net.cn

考虑实际应用中的第一个用例,它需要DataSource.在测试环境中,配置可能类似于以下内容:spring-doc.cadn.net.cn

@Bean
public DataSource dataSource() {
	return new EmbeddedDatabaseBuilder()
		.setType(EmbeddedDatabaseType.HSQL)
		.addScript("my-schema.sql")
		.addScript("my-test-data.sql")
		.build();
}
@Bean
fun dataSource(): DataSource {
	return EmbeddedDatabaseBuilder()
			.setType(EmbeddedDatabaseType.HSQL)
			.addScript("my-schema.sql")
			.addScript("my-test-data.sql")
			.build()
}

现在考虑如何将此应用程序部署到 QA 或生产环境中 环境中,假设应用程序的数据源已注册 替换为生产应用程序服务器的 JNDI 目录。我们dataSource豆 现在如下面的清单所示:spring-doc.cadn.net.cn

@Bean(destroyMethod = "")
public DataSource dataSource() throws Exception {
	Context ctx = new InitialContext();
	return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
	val ctx = InitialContext()
	return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
}

问题在于如何根据 当前环境。随着时间的推移,Spring 用户已经设计出了许多方法来 完成此作,通常依赖于系统环境变量的组合 和 XML<import/>包含${placeholder}解析 添加到正确的配置文件路径,具体取决于环境的值 变量。Bean 定义配置文件是一个核心容器功能,它提供了一个 解决这个问题。spring-doc.cadn.net.cn

如果我们概括前面特定于环境的 bean 示例中显示的用例 定义,我们最终需要在 某些上下文,但在其他上下文中没有。您可以说您想要注册一个 情况 A 中 bean 定义的某个配置文件和 情况 B.我们首先更新配置以反映此需求。spring-doc.cadn.net.cn

@Profile

@Profileannotation 允许您指示组件符合注册条件 当一个或多个指定的配置文件处于活动状态时。使用前面的示例,我们 可以重写dataSource配置如下:spring-doc.cadn.net.cn

@Configuration
@Profile("development")
public class StandaloneDataConfig {

	@Bean
	public DataSource dataSource() {
		return new EmbeddedDatabaseBuilder()
			.setType(EmbeddedDatabaseType.HSQL)
			.addScript("classpath:com/bank/config/sql/schema.sql")
			.addScript("classpath:com/bank/config/sql/test-data.sql")
			.build();
	}
}
@Configuration
@Profile("development")
class StandaloneDataConfig {

	@Bean
	fun dataSource(): DataSource {
		return EmbeddedDatabaseBuilder()
				.setType(EmbeddedDatabaseType.HSQL)
				.addScript("classpath:com/bank/config/sql/schema.sql")
				.addScript("classpath:com/bank/config/sql/test-data.sql")
				.build()
	}
}
@Configuration
@Profile("production")
public class JndiDataConfig {

	@Bean(destroyMethod = "") (1)
	public DataSource dataSource() throws Exception {
		Context ctx = new InitialContext();
		return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
	}
}
1 @Bean(destroyMethod = "")禁用默认销毁方法推理。
@Configuration
@Profile("production")
class JndiDataConfig {

	@Bean(destroyMethod = "") (1)
	fun dataSource(): DataSource {
		val ctx = InitialContext()
		return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
	}
}
1 @Bean(destroyMethod = "")禁用默认销毁方法推理。
如前所述,使用@Bean方法,您通常选择使用 Programmatic JNDI 查找,通过使用 Spring 的JndiTemplate/JndiLocatorDelegatehelpers 或 直 JNDIInitialContext用法,但未显示JndiObjectFactoryBeanvariant 的 Variant 中,这将强制您将返回类型声明为FactoryBean类型。

配置文件字符串可以包含一个简单的配置文件名称(例如production) 或 profile 表达式。配置文件表达式允许更复杂的配置文件逻辑 表示的(例如,production & us-east).支持以下运算符 配置文件表达式:spring-doc.cadn.net.cn

您不能混合使用 和&|运算符。例如production & us-east | eu-central不是有效的表达式。它必须表示为production & (us-east | eu-central).

您可以使用@Profile作为元注释 创建自定义组合注释。以下示例定义了一个自定义@Production注释,您可以将其用作@Profile("production"):spring-doc.cadn.net.cn

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Profile("production")
annotation class Production
如果@Configuration类标有@Profile、所有@Beanmethods 和@Import与该类关联的注释将被绕过,除非一个或多个 指定的配置文件处于活动状态。如果@Component@Configuration类被标记 跟@Profile({"p1", "p2"}),则不会注册或处理该类,除非 配置文件“P1”或“P2”已激活。如果给定配置文件的前缀为 NOT 运算符 (!),则仅当配置文件未 积极。例如,给定@Profile({"p1", "!p2"}),如果配置文件 “p1”处于活动状态,或者配置文件“p2”未处于活动状态。

@Profile也可以在方法级别声明以仅包含一个特定的 bean 配置类中(例如,对于特定 Bean 的替代变体),作为 以下示例显示:spring-doc.cadn.net.cn

@Configuration
public class AppConfig {

	@Bean("dataSource")
	@Profile("development") (1)
	public DataSource standaloneDataSource() {
		return new EmbeddedDatabaseBuilder()
			.setType(EmbeddedDatabaseType.HSQL)
			.addScript("classpath:com/bank/config/sql/schema.sql")
			.addScript("classpath:com/bank/config/sql/test-data.sql")
			.build();
	}

	@Bean("dataSource")
	@Profile("production") (2)
	public DataSource jndiDataSource() throws Exception {
		Context ctx = new InitialContext();
		return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
	}
}
1 standaloneDataSource方法仅在development轮廓。
2 jndiDataSource方法仅在production轮廓。
@Configuration
class AppConfig {

	@Bean("dataSource")
	@Profile("development") (1)
	fun standaloneDataSource(): DataSource {
		return EmbeddedDatabaseBuilder()
				.setType(EmbeddedDatabaseType.HSQL)
				.addScript("classpath:com/bank/config/sql/schema.sql")
				.addScript("classpath:com/bank/config/sql/test-data.sql")
				.build()
	}

	@Bean("dataSource")
	@Profile("production") (2)
	fun jndiDataSource() =
		InitialContext().lookup("java:comp/env/jdbc/datasource") as DataSource
}
1 standaloneDataSource方法仅在development轮廓。
2 jndiDataSource方法仅在production轮廓。

@Profile@Bean方法,特殊情况可能适用:在 重载@Bean方法(类似于构造函数 overloading)、一个@Profilecondition 需要对所有 重载方法。如果条件不一致,则仅 重载方法中的第一个声明很重要。因此@Profile能 不用于选择具有特定参数签名的重载方法 另一个。同一 bean 的所有工厂方法之间的解析遵循 Spring 的 构造函数解析算法。spring-doc.cadn.net.cn

如果要定义具有不同性能分析条件的替代 bean, 通过使用@Bean名字 属性,如前面的示例所示。如果参数签名全部为 相同(例如,所有变体都有 no-arg 工厂方法),这是唯一的 首先在有效的 Java 类中表示这种排列的方式 (因为只能有一个特定名称和参数签名的方法)。spring-doc.cadn.net.cn

XML Bean 定义配置文件

XML 对应项是profile属性的<beans>元素。我们前面的示例 配置可以重写为两个 XML 文件,如下所示:spring-doc.cadn.net.cn

<beans profile="development"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xsi:schemaLocation="...">

	<jdbc:embedded-database id="dataSource">
		<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
		<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
	</jdbc:embedded-database>
</beans>
<beans profile="production"
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xsi:schemaLocation="...">

	<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>

也可以避免这种 split 和 nest<beans/>元素, 如下例所示:spring-doc.cadn.net.cn

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xsi:schemaLocation="...">

	<!-- other bean definitions -->

	<beans profile="development">
		<jdbc:embedded-database id="dataSource">
			<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
			<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
		</jdbc:embedded-database>
	</beans>

	<beans profile="production">
		<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
	</beans>
</beans>

spring-bean.xsd已被约束为仅允许 文件中的最后一个。这应该有助于提供灵活性,而不会产生 XML 文件中的混乱。spring-doc.cadn.net.cn

XML 对应项不支持前面描述的配置文件表达式。有可能, 但是,要使用!算子。也可以应用逻辑 “and” 嵌套配置文件,如下例所示:spring-doc.cadn.net.cn

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xsi:schemaLocation="...">

	<!-- other bean definitions -->

	<beans profile="production">
		<beans profile="us-east">
			<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
		</beans>
	</beans>
</beans>

在前面的示例中,dataSource如果productionus-east配置文件处于活动状态。spring-doc.cadn.net.cn

激活配置文件

现在我们已经更新了配置,我们仍然需要指示 Spring 哪个 配置文件处于活动状态。如果我们现在启动示例应用程序,我们将看到 一个NoSuchBeanDefinitionExceptionthrown,因为容器找不到 名为dataSource.spring-doc.cadn.net.cn

可以通过多种方式激活配置文件,但最直接的是 它以编程方式针对EnvironmentAPI 的 API 可通过ApplicationContext.以下示例显示了如何执行此作:spring-doc.cadn.net.cn

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
val ctx = AnnotationConfigApplicationContext().apply {
	environment.setActiveProfiles("development")
	register(SomeConfig::class.java, StandaloneDataConfig::class.java, JndiDataConfig::class.java)
	refresh()
}

此外,您还可以通过spring.profiles.active属性,可以通过系统环境指定 中的变量、JVM 系统属性、Servlet 上下文参数web.xml,甚至作为 条目(参见PropertySource抽象化).在集成测试中,active 配置文件可以使用@ActiveProfiles注解中的spring-test模块(请参阅 Context Configuration with Environment Profiles )。spring-doc.cadn.net.cn

请注意,用户档案不是“非此即彼”的命题。您可以激活多个 配置文件。以编程方式,您可以向setActiveProfiles()方法,它接受String…​varargs 的以下示例 激活多个配置文件:spring-doc.cadn.net.cn

ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
ctx.getEnvironment().setActiveProfiles("profile1", "profile2")

声明式地,spring.profiles.active可以接受以逗号分隔的配置文件名称列表, 如下例所示:spring-doc.cadn.net.cn

-Dspring.profiles.active="profile1,profile2"

默认配置文件

default 配置文件表示在没有活动配置文件时启用的配置文件。考虑 以下示例:spring-doc.cadn.net.cn

@Configuration
@Profile("default")
public class DefaultDataConfig {

	@Bean
	public DataSource dataSource() {
		return new EmbeddedDatabaseBuilder()
			.setType(EmbeddedDatabaseType.HSQL)
			.addScript("classpath:com/bank/config/sql/schema.sql")
			.build();
	}
}
@Configuration
@Profile("default")
class DefaultDataConfig {

	@Bean
	fun dataSource(): DataSource {
		return EmbeddedDatabaseBuilder()
				.setType(EmbeddedDatabaseType.HSQL)
				.addScript("classpath:com/bank/config/sql/schema.sql")
				.build()
	}
}

如果没有配置文件处于活动状态,则dataSource是 创建。您可以将此视为为一个或多个 豆。如果启用了任何配置文件,则默认配置文件不适用。spring-doc.cadn.net.cn

默认配置文件的名称为default.您可以更改 默认配置文件setDefaultProfiles()Environment或 以声明方式使用spring.profiles.default财产。spring-doc.cadn.net.cn

PropertySource抽象化

Spring的Environmentabstraction 通过可配置的 属性源的层次结构。请考虑以下清单:spring-doc.cadn.net.cn

ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
val ctx = GenericApplicationContext()
val env = ctx.environment
val containsMyProperty = env.containsProperty("my-property")
println("Does my environment contain the 'my-property' property? $containsMyProperty")

在前面的代码片段中,我们看到了一种高级方式,即询问 Spring 的my-propertyproperty 为 为当前环境定义。要回答这个问题,Environment对象执行 对一组PropertySource对象。一个PropertySource是对任何键值对源的简单抽象,并且 Spring的StandardEnvironment配置了两个 PropertySource 对象 — 一个表示 JVM 系统属性集 (System.getProperties()) 和一个表示系统环境变量集 (System.getenv()).spring-doc.cadn.net.cn

这些默认属性源适用于StandardEnvironment,用于独立 应用。StandardServletEnvironment填充了其他默认属性源,包括 Servlet Config、Servlet context 参数和JndiPropertySource如果 JNDI 可用。

具体来说,当您使用StandardEnvironment中,调用env.containsProperty("my-property")如果my-propertysystem 属性或my-property环境变量位于 运行。spring-doc.cadn.net.cn

执行的搜索是分层的。默认情况下,系统属性优先于 环境变量。因此,如果my-propertyproperty 恰好在两个地方都设置了 调用env.getProperty("my-property"),则系统属性值 “wins” 并返回。 请注意,属性值不会合并 而是完全被前面的条目覆盖。spring-doc.cadn.net.cn

对于常见的StandardServletEnvironment,则完整的层次结构如下所示,其中 topest-precedence 条目:spring-doc.cadn.net.cn

  1. ServletConfig 参数(如果适用 — 例如,如果DispatcherServlet上下文)spring-doc.cadn.net.cn

  2. ServletContext 参数(web.xml context-param 条目)spring-doc.cadn.net.cn

  3. JNDI 环境变量 (java:comp/env/条目)spring-doc.cadn.net.cn

  4. JVM 系统属性 (-D命令行参数)spring-doc.cadn.net.cn

  5. JVM 系统环境(作系统环境变量)spring-doc.cadn.net.cn

最重要的是,整个机制是可配置的。也许您有一个自定义源 要集成到此搜索中的属性。为此,请实现 并实例化您自己的PropertySource并将其添加到PropertySources对于 当前Environment.以下示例显示了如何执行此作:spring-doc.cadn.net.cn

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
val ctx = GenericApplicationContext()
val sources = ctx.environment.propertySources
sources.addFirst(MyPropertySource())

在上面的代码中,MyPropertySource已在 搜索。如果它包含my-propertyproperty,则会检测并返回该属性,以支持 任何my-propertyproperty 在任何其他PropertySource.这MutablePropertySourcesAPI 公开了许多方法,这些方法允许精确作 property 源。spring-doc.cadn.net.cn

@PropertySource

@PropertySource注解提供了一种方便的声明式机制,用于添加PropertySource到 Spring 的Environment.spring-doc.cadn.net.cn

给定一个名为app.properties,其中包含键值对testbean.name=myTestBean, 以下@Configuration类用途@PropertySource以这种方式, 调用testBean.getName()返回myTestBean:spring-doc.cadn.net.cn

@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {

 @Autowired
 Environment env;

 @Bean
 public TestBean testBean() {
  TestBean testBean = new TestBean();
  testBean.setName(env.getProperty("testbean.name"));
  return testBean;
 }
}
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
class AppConfig {

	@Autowired
	private lateinit var env: Environment

	@Bean
	fun testBean() = TestBean().apply {
		name = env.getProperty("testbean.name")!!
	}
}

任何${…​}placeholder 存在于@PropertySourceresource location 为 针对已针对 environment,如下例所示:spring-doc.cadn.net.cn

@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {

 @Autowired
 Environment env;

 @Bean
 public TestBean testBean() {
  TestBean testBean = new TestBean();
  testBean.setName(env.getProperty("testbean.name"));
  return testBean;
 }
}
@Configuration
@PropertySource("classpath:/com/\${my.placeholder:default/path}/app.properties")
class AppConfig {

	@Autowired
	private lateinit var env: Environment

	@Bean
	fun testBean() = TestBean().apply {
		name = env.getProperty("testbean.name")!!
	}
}

假设my.placeholder已存在于其中一个属性源中 registered (例如,系统属性或环境变量),则占位符为 resolved 的值。如果不是,则default/path已使用 作为默认值。如果未指定 default 且无法解析属性,则IllegalArgumentException被抛出。spring-doc.cadn.net.cn

@PropertySource可用作可重复的注释。@PropertySource也可以用作元注释,以使用 属性覆盖。

语句中的占位符解析

从历史上看,元素中占位符的值只能针对 JVM 系统属性或环境变量。现在情况已不再如此。因为 这Environmentabstraction 集成在整个容器中,因此很容易 通过它的 route resolution of placeholders 进行路由解析。这意味着您可以配置 解决过程。您可以更改搜索的优先级 系统属性和环境变量,或者完全删除它们。您还可以添加 自己的属性源添加到组合中。spring-doc.cadn.net.cn

具体来说,以下语句无论customer属性,只要它在Environment:spring-doc.cadn.net.cn

<beans>
	<import resource="com/bank/service/${customer}-config.xml"/>
</beans>