1.13。Environment 抽象
Environment
接口是集成在容器中的抽象,可以对应用程序环境的两个关键方面进行建模:profile
和 properties
。
profile
是仅在给定 profile 处于活动状态时才向容器注册的Bean定义的命名逻辑组。可以将Bean分配给 profile ,无论是以XML定义还是带有注释。Environment对象与 profile 相关的作用是确定当前哪些 profile (如果有)处于活动状态,以及默认情况下哪些 profile (如果有)应处于活动状态。
属性在几乎所有应用程序中都起着重要作用,并且可能源自各种来源:属性文件,JVM系统属性,系统环境变量,JNDI,Servlet上下文参数,Properties对象,Map对象等。Environment对象与属性有关的角色是为用户提供方便的服务界面,用于配置属性源并从中解析属性。
1.13.1。Bean定义 Profiles
使用 @Profile
profile 字符串可以包含简单的 profile 名称(例如production)或 profile 表达式。 profile 表达式允许表达更复杂的配置文件逻辑(例如production & us-east
)。概要文件表达式中支持以下运算符:
!
:逻辑否&
:逻辑与|
:逻辑或
您不能在不使用括号的情况下混合使用&and |运算符。例如, production & us-east | eu-central
不是有效的表达式。它必须表示为 production & (us-east | eu-central)
。
您可以将@Profile
用作元注释,以创建自定义的组合注释。以下示例定义了一个自定义 @Production
批注,您可以将其用作@Profile("production")
替代品 :
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
如果用@Profile
标记一个@Configuration
类,则除非一个或多个指定的 profile 处于活动状态,否则将忽略与该类关联的所有@Bean
方法和 @Import
注释。如果一个@Component
或@Configuration
类标记有@Profile({"p1", "p2"})
,则除非已激活 profile “p1”或“p2”,否则该类不会注册或处理。如果给定的配置文件以NOT运算符(!
)为前缀,则仅在该 profile 未激活时才注册带注释的元素。例如,给定@Profile({"p1", "!p2"})
,如果 profile “p1”处于活动状态或 profile “p2”未处于活动状态,则会进行注册。
@Profile 也可以在方法级别声明为仅包括配置类的一个特定Bean
@Bean方法使用@Profile时,可能会出现特殊情况:对于具有相同Java方法名称的重载@Bean方法(类似于构造函数重载),@Profile需要在所有重载方法上一致声明条件。如果条件不一致,则仅重载方法中第一个声明的条件很重要。因此,@Profile不能用于选择具有特定参数签名的重载方法。在创建时,相同bean的所有工厂方法之间的解析都遵循Spring的构造函数解析算法。
如果要使用不同的 profile 条件定义备用bean,请使用不同的Java方法名称,这些名称通过使用@Bean name属性指向相同的bean名称,如前面的示例所示。如果参数签名都相同(例如,所有变体都具有无参工厂方法),则这是首先在有效Java类中表示这种排列的唯一方法(因为只能有一个特定名称和参数签名的方法)。
XML Bean定义 Profile
XML对应项是
元素的profile
属性。
XML对应项不支持前面描述的 profile 表达式。但是,可以通过使用!
运算符来取消 profile 。也可以通过嵌套 profile 来应用逻辑“和”,如以下示例所示:
在前面的示例中,如果production和 us-east profile 都处于活动状态,则该dataSource bean是公开的。
激活 Profile
最直接的方法是针对通过ApplicationContext Environment API以编程方式进行配置。
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
还可以通过spring.profiles.active
属性声明性地激活配置文件,可以通过系统环境变量,JVM系统属性,web.xml中的servlet上下文参数或作为JNDI中的条目来指定 profile 。在集成测试中,可以通过使用spring-test
模块中的@ActiveProfiles
注释来声明活动 profile。
可以一次激活多个 profile 。通过编程,您可以为setActiveProfiles()
方法提供多个 profile 名称
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
以声明方式,spring.profiles.active
可以接受以逗号分隔的配置文件名称列表
-Dspring.profiles.active="profile1,profile2"
默认Profile
默认的profile为default
。
您可以通过Environment
上的setDefaultProfiles()
更改默认的 profile 的名称,或者通过使用spring.profiles.default
属性。
1.13.2。PropertySource 抽象
Spring的Environment抽象提供了对属性源可配置层次结构的搜索操作。
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);
在前面的代码片段中,我们看到了一种询问Spring是否为当前环境定义了my-property属性的高级方法。为了回答这个问题,Environment
对象在一组PropertySource
对象上执行搜索。PropertySource是对任何键-值对源的简单抽象,Spring的StandardEnvironment 配置有两个PropertySource对象,一个代表JVM系统属性集(System.getProperties()
),一个代表系统环境变量集(System.getenv()
)。
这些默认属性源存在于StandardEnvironment中,供在独立应用程序中使用。StandardServletEnvironment 用其他默认属性源(包括servlet config和servlet context参数)填充。它可以有选择地启用JndiPropertySource。
具体来说,当您使用StandardEnvironment时, 如果运行时存在my-property系统属性或my-property环境变量,则对env.containsProperty("my-property")
的调用将返回true 。
执行的搜索是分层的。默认情况下,系统属性优先于环境变量。因此,如果my-property在调用期间在两个地方同时设置了env.getProperty("my-property")
属性,则系统属性值将“获胜”并返回。请注意,属性值不会合并,而是会被前面的条目完全覆盖。
对于common StandardServletEnvironment,完整层次结构如下,最高优先级条目位于顶部:
ServletConfig
参数(如果适用,例如在DispatcherServlet上下文的情况下)ServletContext
参数(web.xml上下文参数条目)- JNDI环境变量(java:comp/env/条目)
- JVM系统属性(-D命令行参数)
- JVM系统环境(操作系统环境变量)
最重要的是,整个机制是可配置的。也许您有一个要集成到此搜索中的自定义属性源。为此,请实现并实例化自己的实例PropertySource并将其添加到当前Environment的PropertySources 集合中。以下示例显示了如何执行此操作:
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
在前面的代码中,MyPropertySource已在搜索中添加了最高优先级。如果它包含一个my-property属性,则检测并返回该属性,以支持其他PropertySource的my-property属性。MutablePropertySources
API公开了许多方法,这些方法允许对属性源集进行精确操作。
1.13.3。使用@PropertySource
@PropertySource 注解提供便利和声明的机制添加PropertySource 到Spring的Environment。
@PropertySource("classpath:/com/myco/app.properties")
@PropertySource
资源位置中存在的所有${..}
占位符都是根据已经针对环境注册的一组属性源来解析的
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
假设my.placeholder存在于已注册的属性源之一(例如,系统属性或环境变量)中,则占位符将解析为相应的值。如果不是,则default/path
用作默认值。如果未指定默认值并且无法解析属性, 则抛出IllegalArgumentException
。
@PropertySource
注释是可重复的,根据Java 8的约定。但是,所有此类@PropertySource批注都需要在同一级别上声明,可以直接在配置类上声明,也可以在同一自定义批注中声明为元批注。不建议将直接注释和元注释混合使用,因为直接注释会有效地覆盖元注释。
1.13.4。声明中的占位符解析
从历史上看,元素中占位符的值只能根据JVM系统属性或环境变量来解析。这已不再是这种情况。由于Environment抽象是在整个容器中集成的,因此很容易通过它路由占位符的解析。这意味着您可以按照自己喜欢的任何方式配置解析过程。您可以更改搜索系统属性和环境变量的优先级,也可以完全删除它们。您还可以根据需要将自己的属性源添加到混合中。
具体而言,无论customer 属性在何处定义,以下语句均有效,只要该属性在Environment中可用:
1.14。注册一个LoadTimeWeaver
当LoadTimeWeaver
类加载到Java虚拟机(JVM)中时,Spring会使用来动态转换类。
要启用加载时编织,可以将@EnableLoadTimeWeaving
添加到您的一个 @Configuration
类,如以下示例所示:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
另外,对于XML配置,可以使用context:load-time-weaver
元素:
一旦针对ApplicationContext
进行了配置,则ApplicationContext中的任何bean都可以实现LoadTimeWeaverAware
,从而接收到对加载时编织器实例的引用。与Spring的JPA支持结合使用时,该功能特别有用, 因为在进行JPA类转换时可能需要进行加载时编织。