对于最新稳定版本,请使用Spring Framework 7.0.1spring-doc.cadn.net.cn

环境抽象

环境接口 是集成在容器中的抽象,模拟两个密钥 应用环境的各个方面:配置文件属性spring-doc.cadn.net.cn

配置文件是一组有名称的逻辑豆定义,需注册于 只有当该配置文件处于激活状态时才使用容器。豆子可以被分配到配置文件 无论是用XML定义还是带注释的。该职位的作用环境对象 与配置文件的关系在于确定当前活跃的配置文件(如果有的话), 以及哪些配置文件(如果有的话)默认应该激活。spring-doc.cadn.net.cn

属性在几乎所有应用中都扮演重要角色,可能起源于 多种来源:属性文件、JVM 系统属性、系统环境 变量、JNDI、servlet 上下文参数、临时调用性能对象地图对象,因此 上。该职位的作用环境与属性相关的对象是 用户拥有便捷的服务界面,用于配置属性源和解析 这些财产。spring-doc.cadn.net.cn

Beans定义配置文件

豆定义配置文件在核心容器中提供了一种机制,允许 不同Beans在不同环境中的注册。“环境”这个词, 对不同用户来说可能有不同的含义,这个功能能帮助很多人 使用场景包括:spring-doc.cadn.net.cn

考虑一个实际应用中的第一个用例,需要数据来源.在测试环境中,配置可能如下: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()
}

现在考虑该应用如何部署到质量保证或生产环境中 假设应用程序的数据源已注册 与生产应用服务器的 JNDI 目录相符。我们数据来源豆 现在看起来是这样的列表: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/>包含以下的语句${占位符}结算的标记 根据环境的值,选择正确的配置文件路径 变量。Bean 定义配置文件是核心容器功能,提供 解决这个问题。spring-doc.cadn.net.cn

如果我们推广前面示例中展示的环境特定豆的用例 定义,我们最终需要注册某些豆子定义 某些情境下,但另一些则不然。你可以说你想注册一个 在情境A中,Beans定义的某些轮廓,以及在不同的轮廓中 情况B。我们首先更新配置以反映这一需求。spring-doc.cadn.net.cn

@Profile

@Profile注释功能可以让你表明某个组件符合注册资格 当一个或多个指定配置文件处于激活状态时。使用我们之前的例子,我们 可以重写数据来源配置如下: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方法,通常选择使用程序化 JNDI查询,使用任一SpringJndiTemplate/JndiLocatorDelegate助手或 纯正的JNDI初始上下文之前显示的用法,但未显示JndiObjectFactoryBean变体,这会强制你将返回类型声明为工厂豆类型。

配置文件字符串可能包含一个简单的配置文件名称(例如,生产)或 侧脸表情。配置文件表达式允许更复杂的配置文件逻辑为 表达式(例如,制作与美国东部).以下作符支持于 个人资料表达:spring-doc.cadn.net.cn

你不能把和混合在一起&|不使用括号的运算符。例如制作与美国东部 |欧盟中部不是一个有效的表达。它必须表示为生产 &(美国东部 | 欧盟中部).

你可以使用@Profile作为一种元注释 创建自定义注释。以下示例定义了自定义@Production你可以直接替换的注释@Profile(“制作”):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,所有@Bean方法和@Import与该类相关的注释除非有一个或多个 指定的配置文件是激活的。如果@Component@Configuration等级被标记 跟@Profile({“p1”, “p2”})该类别除非被注册或处理 “P1”或“P2”配置文件已被激活。如果给定的配置文件前缀为 NOT 算符 (!),只有当该配置文件未被标记时,注释元素才会被注册 积极。例如,给定@Profile({“p1”, “!p2”}),注册将在档案中进行 “p1”是激活的,或者如果配置文件“p2”未激活。

@Profile也可以在方法层面声明只包含一个特定的豆子 对于配置类(例如,对于特定豆子的其他变体),定义为 以下示例展示了: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 独立数据源该方法仅适用于发展轮廓。
2 jndiDataSource该方法仅适用于生产轮廓。
@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 独立数据源该方法仅适用于发展轮廓。
2 jndiDataSource该方法仅适用于生产轮廓。

@Profile@Bean方法,可能适用一种特殊情形:在 重载@Bean同一 Java 方法名的方法(类似于构造函数) 过载),a@Profile条件需要在所有系统上一致声明 过载的方法。如果条件不一致,只有条件 过载方法中的首次声明很重要。因此@Profile能 不可用于选择具有特定参数签名的超载方法。 另一个。同一豆子所有工厂方法之间的分辨遵循Spring的 构建器解析算法在创建时。spring-doc.cadn.net.cn

如果你想定义具有不同轮廓条件的替代豆子, 使用指向同一豆子名称的不同 Java 方法名,方法是使用@Bean名字 属性,如前例所示。如果所有参数签名都是 同样(例如,所有变体都有无 ARG 工厂方法),这是唯一的 如何首先在有效的 Java 类中表示这种排列 (因为特定名称和参数签名只能有一个方法)。spring-doc.cadn.net.cn

XML Bean 定义配置文件

XML对应的是轮廓属性<豆子>元素。我们之前的样本 配置可以用两个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>

也可以避免这种分裂和筑巢<豆子/>同一文件中的元素, 如下示例所示: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>

在前面的例子中,数据来源如果有生产美国东部个人资料已激活。spring-doc.cadn.net.cn

激活个人资料

现在我们已经更新了配置,仍然需要指示 Spring 个人资料已激活。如果我们现在就开始做样应用,就能看到 一个NoSuchBeanDefinitionException扔掉了,因为容器找不到 春季豆数据来源.spring-doc.cadn.net.cn

激活个人资料有多种方式,但最直接的做法是 它在程序上针对环境API通过以下方式提供应用上下文.以下示例展示了如何实现: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()
}

此外,你还可以通过春季.档案.活跃属性,可以通过系统环境指定 变量、JVM 系统属性、servlet 上下文参数web.xml,甚至作为 JNDI条目(参见地产来源抽象化).在积分测试中,主动 配置文件可以通过以下方式声明@ActiveProfiles注释春季测试模块(参见带有环境配置文件的上下文配置)。spring-doc.cadn.net.cn

请注意,画像不是“非此即彼”的选择。你可以激活多个 同时进行多个档案。在程序上,你可以为setActiveProfiles()方法,接受字符串。。。瓦拉格。以下示例 激活多个配置文件:spring-doc.cadn.net.cn

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

声明性地,春季.档案.活跃可以接受逗号分隔的配置文件名称列表, 如下示例所示:spring-doc.cadn.net.cn

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

默认配置文件

默认配置文件代表如果没有激活配置文件,该配置文件已启用。考虑 以下示例: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()
	}
}

如果没有配置文件激活,数据来源是 创建。你可以把这看作是为一个或多个提供默认定义 豆。如果启用了任何配置文件,默认配置文件就不适用。spring-doc.cadn.net.cn

默认配置文件的名称为默认值.你可以更改名称 默认配置文件通过使用setDefaultProfiles()环境或 声明式地,通过使用spring.profiles.default财产。spring-doc.cadn.net.cn

地产来源抽象化

斯普林斯环境抽象提供可配置的搜索作 财产来源层级。请考虑以下列表: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")

在前面的片段中,我们看到一个高层次的方式,询问斯普林是否我的财产财产是 为当前环境定义。回答这个问题,环境对象执行 对一组地产来源对象。一个地产来源是任意键值对源的简单抽象,且 斯普林斯标准环境配置为两个 PropertySource 对象——其中一个代表 JVM 系统属性集合 (System.getProperties())以及一个表示系统环境变量集合的 (System.getenv()).spring-doc.cadn.net.cn

这些默认属性源存在于标准环境,用于独立用途 应用。StandardServletEnvironment填充了额外的默认属性源,包括servlet config、servlet 上下文参数,以及JndiPropertySource如果JNDI可用。

具体来说,当你使用标准环境,呼叫env.containsProperty(“我的财产”)如果我的财产系统性质或我的财产环境变量 位于 运行。spring-doc.cadn.net.cn

搜索是层级的。默认情况下,系统属性优先于系统属性 环境变量。所以,如果我的财产该财产恰好设定在这两地 呼唤env.getProperty(“my-property”),系统属性值“获胜”并返回。 注意,属性值不会合并 而是被前一条完全覆盖。spring-doc.cadn.net.cn

对于一个公共StandardServletEnvironment,完整层级如下,其中 最高优先级条目位于顶部:spring-doc.cadn.net.cn

  1. ServletConfig 参数(如适用——例如,在调度器服务背景)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

最重要的是,整个机制是可配置的。也许你有定制的来源 这些属性你想整合进这个搜索中。为此,实现 并实例化你自己的地产来源并将其加入地产来源对于 当前环境.以下示例展示了如何实现: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())

在前述法典中,我的财产源在 中以最高优先级被添加 搜索。如果它包含我的财产财产,财产被检测并归还,使得 任何我的财产其他任何财产地产来源.这可变属性源API提供了多种方法,允许对 集合进行精确作。 财产来源。spring-doc.cadn.net.cn

@PropertySource

@PropertySource注释提供了一种方便且声明式的机制来添加地产来源去Spring的环境.spring-doc.cadn.net.cn

给定一个名为app.properties包含键值对testbean.name=myTestBean, 以下内容@Configuration类别用途@PropertySource以一种方式 呼唤testBean.getName()返回我的测试豆: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")!!
	}
}

任何${…​}存在于@PropertySource资源位置为 针对已登记的财产来源的一组,已通过该项决议 环境,如下例子所示: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")!!
	}
}

假设我的占位符已经存在于其中一个属性源中 注册(例如系统属性或环境变量),占位符为 解析为相应的值。如果不行,那默认/路径被使用 默认情况下。如果没有指定默认值且无法解决某个属性,则IllegalArgumentException被抛出。spring-doc.cadn.net.cn

@PropertySource可以作为可重复的注释使用。@PropertySource也可以用作元注释,创建自定义组合注释,使用 属性覆盖。

语句中的占位符解析

历史上,元素占位符的值只能与 JVM 系统属性或环境变量。但现在情况已不同。因为 这环境抽象集成在整个容器中,很容易 通过它路由占位符解析。这意味着你可以配置 解决过程可以随你喜欢。你可以改变搜索的优先顺序 系统属性和环境变量,或者完全移除它们。你也可以添加你的 根据需要,拥有自己的地产来源。spring-doc.cadn.net.cn

具体来说,以下陈述无论在哪里都成立客户端只要属性存在于环境:spring-doc.cadn.net.cn

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