本文內容
- Environment抽象的2個重要概念
- @Profile 的使用
- @PropertySource 的使用
Environment抽象的2個重要概念
Environment 介面表示當前應用程式運行環境的介面,對應用程式環境的兩個關鍵方面進行建模:組態檔( profiles )和屬性(properties),與屬性訪問相關的方法通過 PropertyResolver 超介面公開,環境物件的配置必須通過 ConfigurableEnvironment 介面完成,該介面從所有 AbstractApplicationContext 子類 getEnvironment() 方法回傳
環境與組態檔
組態檔是一個命名的、邏輯的 bean 定義組,僅當給定的組態檔處于活動狀態時才向容器注冊,可以將 Bean 分配給組態檔,無論是在 XML 中定義還是通過注釋 @Profile 定義;與組態檔相關的環境物件的作用是確定哪些組態檔(如果有)當前處于活動狀態,以及哪些組態檔(如果有)默認應該是活動的,
環境與屬性
屬性在幾乎所有應用程式中都發揮著重要作用,并且可能源自多種來源:屬性檔案、JVM 系統屬性、系統環境變數、JNDI、servlet 背景關系引數、屬性物件、map等,與屬性相關的環境物件的作用是為用戶提供一個方便的服務介面,用于配置屬性源并從中決議屬性,
在 ApplicationContext 中管理的 Bean 可以注冊為 EnvironmentAware 或 @Inject Environment,以便直接查詢組態檔狀態或決議屬性,然而,在大多數情況下,應用程式級別的 bean 不需要直接與 Environment 互動,而是可能必須將 ${...} 屬性值替換為屬性占位符配置器,例如 PropertySourcesPlaceholderConfigurer,它本身是 EnvironmentAware 并且從 Spring 3.1 開始使用 context:property-placeholder 時默認注冊 ,或是通過java bean的方式注冊到容器中,
PropertySourcesPlaceholderConfigurer 分析可以閱讀上一篇: Spring系列14:IoC容器的擴展點
介面原始碼粗覽
介面繼承關系

介面原始碼如下提供組態檔相關的介面方法,其繼承的 PropertyResolver 提供屬性相關的介面,
public interface Environment extends PropertyResolver {
// 當前激活的組態檔串列
// 設定系統屬性值 spring.profiles.active=xxx 可激活
// 或是呼叫 ConfigurableEnvironment#setActiveProfiles(String...)激活
String[] getActiveProfiles();
// 當沒有明確設定活動組態檔時,默認組態檔集回傳為活動狀態,
String[] getDefaultProfiles();
// 回傳活動組態檔是否與給定的 Profiles 匹配
boolean acceptsProfiles(Profiles profiles);
}
PropertyResolver 是針對任何底層源決議屬性的介面,主要介面方法如下,有一個非常重要的實作類是 PropertySourcesPlaceholderConfigurer ,
public interface PropertyResolver {
// 是否包含屬性
boolean containsProperty(String key);
// 獲取屬性值
String getProperty(String key);
// 獲取屬性值帶默認值
String getProperty(String key, String defaultValue);
// 獲取屬性值
<T> T getProperty(String key, Class<T> targetType);
// 獲取屬性值帶默認值
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
// 獲取屬性值
String getRequiredProperty(String key) throws IllegalStateException;
// 獲取屬性值
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
// 決議給定文本中的 ${...} 占位符
String resolvePlaceholders(String text);
// 決議給定文本中的 ${...} 占位符
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}
ConfigurablePropertyResolver 是大多數 PropertyResolver 型別都將實作的配置介面,提供用于訪問和自定義將屬性值從一種型別轉換為另一種型別時使用的 ConversionService 的工具,
public interface ConfigurablePropertyResolver extends PropertyResolver {
ConfigurableConversionService getConversionService();
void setConversionService(ConfigurableConversionService conversionService);
// 設定占位符前綴 默認的 "${"怎么來的
void setPlaceholderPrefix(String placeholderPrefix);
// 設定占位符后綴 默認的 "}"怎么來的
void setPlaceholderSuffix(String placeholderSuffix);
// 設定占位符值分分隔符 默認的 ":"怎么來的
void setValueSeparator(@Nullable String valueSeparator);
void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);
void setRequiredProperties(String... requiredProperties);
void validateRequiredProperties() throws MissingRequiredPropertiesException;
}
ConfigurableEnvironment是大多數環境型別都將實作的配置介面,提供用于設定活動和默認組態檔以及操作基礎屬性源的工具,允許客戶端通過 ConfigurablePropertyResolver 超級介面設定和驗證所需屬性、自定義轉換服務等,
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
void setActiveProfiles(String... profiles);
void addActiveProfile(String profile);
void setDefaultProfiles(String... profiles);
MutablePropertySources getPropertySources();
// 關鍵的系統屬性 System#getProperties()
Map<String, Object> getSystemProperties();
// 關鍵的系統環境 System#getenv()
Map<String, Object> getSystemEnvironment();
void merge(ConfigurableEnvironment parent);
}
@Profile 的使用
@Profile 表示當一個或多個profiles處于活動狀態時,組件有資格注冊,可以通過以下的方式設定活躍的一個或是多個組態檔:
- 編程方式:ConfigurableEnvironment#setActiveProfiles(String...)
- 啟動引數: -Dspring.profiles.active="profile1,profile2"
- xml配置方式:
使用案例
來看一個實際場景:不同環境要求在容器中注入不同型別的的資料源,dev環境使用H2,生產環境prod使用Mysql,default環境使用 HSQL,
定義不同環境的資料源,并標識 @Profile
@Configuration
@ComponentScan
public class AppConfig {
// 測驗環境資料源H2
@Profile("dev")
@Bean
public DataSource devDataSource() {
DataSource dataSource = new DataSource();
dataSource.setType("H2");
dataSource.setUrl("jdbc:h2:xxxxxx");
return dataSource;
}
// 生產環境資料源mysql
@Profile("prod")
@Bean
public DataSource prodDataSource() {
DataSource dataSource = new DataSource();
dataSource.setType("mysql");
dataSource.setUrl("jdbc:mysql:xxxxxx");
return dataSource;
}
// default 環境的 HSQL
@Profile("default")
@Bean
public DataSource defaultDataSource() {
DataSource dataSource = new DataSource();
dataSource.setType("HSQL");
dataSource.setUrl("jdbc:HSQL:xxxxxx");
return dataSource;
}
}
測驗程式,首先不指定 profile
@org.junit.Test
public void test_profile() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext();
// context.getEnvironment().setActiveProfiles("prod");
context.register(AppConfig.class);
context.refresh();
DataSource dataSource = context.getBean(DataSource.class);
System.out.println(dataSource.getType());
context.close();
}
// 輸出結果
HSQL
從結果可知,注冊到容器中的 default 環境對應的 HSQL
指定 profile 為 prod ,觀察輸出
context.getEnvironment().setActiveProfiles("prod")
// 結果
mysql
從結果可知,注冊到容器中的 prod 環境對應的 mysql ,
支持邏輯運算子
支持與或非操作組合
- !
- &
- |
組合&和|必須使用小括號
反例:production & us-east | eu-central
正例:production & (us-east | eu-central)
使用 @Profile 自定義組合注解
定義組合注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
使用
@Configuration
@Production
public class MyConfiguration {
}
如果@Configuration 類用@Profile 標記,則與該類關聯的所有@Bean 方法和@Import 注釋都將被繞過,除非一個或多個指定的組態檔處于活動狀態,
使用xml指定 profile
profile元素的可以指定組態檔,
<?xml version="1.0" encoding="UTF-8"?>
<beans profile="prod"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource">
<property name="type" value="https://www.cnblogs.com/kongbubihai/p/mysql"/>
<property name="url" value="https://www.cnblogs.com/kongbubihai/p/jdbc:mysql/xxxxx"/>
</bean>
</beans>
PropertySource 抽象
Spring 的 Environment 抽象在可配置的屬性源層次結構上提供搜索操作,來看下案例如何從Spring 容器獲取屬性,
@org.junit.Test
public void test_property_source() {
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);
}
PropertySource 是對任何鍵值對源的簡單抽象,Spring 的 StandardEnvironment 配置了兩個 PropertySource 物件:
-
一個表示一組 JVM 系統屬性 (System.getProperties())
-
一個表示一組系統環境變數 (System.getenv())
public class StandardEnvironment extends AbstractEnvironment {
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
// 自定義適合任何標準的屬性源自定義一組屬性源
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
}
在屬性源中查找屬性是否存在的優先級順序如下,從高到低:
- ServletConfig parameters (web背景關系)
- ServletContext parameters (web.xml context-param entries)
- JNDI environment variables (
java:comp/env/entries) - JVM system properties (
-Dcommand-line arguments) - JVM system environment (operating system environment variables)
自定義 PropertySource
自定義 MyPropertySource 實作 Property 提供基于 Map 屬性鍵值對的屬性源
/**
* 自定義 PropertySource
* @author zfd
* @version v1.0
* @date 2022/1/22 22:13
* @關于我 請關注公眾號 螃蟹的Java筆記 獲取更多技術系列
*/
public class MyPropertySource extends PropertySource<Map<String, Object>> {
public MyPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
public MyPropertySource(String name) {
super(name);
}
@Override
public Object getProperty(String name) {
return this.source.get(name);
}
}
添加到Spring 容器環境中,優先級最高
@org.junit.Test
public void test_custom_property_source() {
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
Map<String, Object> map = new HashMap<>();
map.put("my-property", "xxx");
sources.addFirst(new MyPropertySource("myPropertySource",map));
// true
boolean containsMyProperty = ctx.getEnvironment().containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
}
@PropertySource 使用
相比上面的編程式添加 PropertySource,@PropertySource 注解為將 PropertySource 添加到 Spring 的環境中提供了一種方便且宣告性的機制,直接看案例,
app.properties配置
testBean.name=xxx
配置類
@Configuration
// 注入組態檔
@PropertySource("classpath:demo13/app.properties")
public class AppConfig3 {
@Autowired
private Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testBean.name"));
return testBean;
}
}
測驗結果觀察
@org.junit.Test
public void test_property_source_annotation() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig3.class);
TestBean testBean = context.getBean(TestBean.class);
System.out.println(testBean.getName());
}
// 結果
xxx
@PropertySource 中指定組態檔也是可以使用占位符${...}的,如果環境中屬性值my.config.path已經存在則進行決議,否則使用默認值demo13,
@Configuration
// 注入組態檔
@PropertySource("classpath:${my.config.path:demo13}/app.properties")
public class AppConfig3 {}
總結
本文介紹了Spring中的Environment抽象的2個重要概念:Bean定義組態檔和屬性源,同時介紹了@Profile使用和@PropertySource 的使用,
本篇原始碼地址: https://github.com/kongxubihai/pdf-spring-series/tree/main/spring-series-ioc/src/main/java/com/crab/spring/ioc/demo13
知識分享,轉載請注明出處,學無先后,達者為先!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/426355.html
標籤:Java
下一篇:2-基本語法
