文章目錄
- 夯實Spring系列|第十五章:Spring 配置元信息
- 本章說明
- 1.項目環境
- 2.Spring 配置元信息
- 3.Spring Bean 配置元信息
- 4.Spring Bean 屬性元信息
- 5.Spring 容器配置元信息
- 6.基於 XML 文件裝載 Spring Bean 配置元信息
- 7.基於 Properties 文件裝載 Spring Bean 配置元信息
- 8.基於 Java 註解裝載 Spring Bean 配置元信息
- 9.Spring Bean 配置元信息底層實現
- 9.1 Spring XML 資源 BeanDefinition 解析與註冊
- 9.2 Spring Properties 資源 BeanDefintion 解析與註冊
- 9.3 Spring Java 註冊 BeanDefinition 解析與註冊
- 10.基於 XML 文件裝載 Spring IOC 容器配置元信息
- 11.基於 Java 註解裝載 Spring IOC 容器配置元信息
- 12.基於 Extensible XML authoring 擴展 Spring XML 元素
- 12.1 編寫 XML Schema 文件
- 12.2 自定義 NamespaceHandler 實現 & 自定義 BeanDefinitionParser 實現
- 12.3 註冊 XML 擴展
- 12.4 示例
- 13.Extensible XML authoring 擴展原理
- 14.基於 Properties 文件裝載外部化配置
- 15.基於 YAML 文件裝載外部化配置
- 16.面試題
- 17.參考
夯實Spring系列|第十五章:Spring 配置元信息
本章說明
本章將詳細的介紹 Spring 中的各類配置元信息,比較重要或者是前面章節沒有討論過的內容,會有簡單的演示代碼;這部分內容雖然在國內討論的熱度不高,但實際上在很多分佈式應用的源碼中處處可見,包括 dubbo、nacos 都有用到其中相關的技術,簡而言之,這一章是學習 Spring 相關分佈式應用技術棧源碼的基礎。
1.項目環境
- jdk 1.8
- spring 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
- 本章模塊:configuration-metadata
2.Spring 配置元信息
配置元信息
- Spring Bean 配置元信息 - BeanDefinition
- Spring Bean 屬性元信息 - PropertiesValues
- Spring 容器配置元信息
- Spring 外部化配置元信息 - PropertySource
- Spring Profile 元信息 - @Profile
3.Spring Bean 配置元信息
Bean 配置元信息 - BeanDefinition
- GenericBeanDefinition : 通用性 BeanDefinition
- RootBeanDefinition : 無 Parent 的 BeanDefinition 或者是 合併後 BeanDefinition
- AnnotatedBeanDefinition : 註解標註的 BeanDefinition
4.Spring Bean 屬性元信息
Bean 屬性元信息 - PropertyValues
- 可修改實現 - MutablePropertyValues
- 元素成員 - PropertyValue
Bean 屬性上下文存儲 - AttributeAccessor
Bean 元信息元素 - BeanMetadataElement
4.1 PropertyValues
第五章:Spring Bean 定義 4.BeanDefinition 構建 小節有相關的例子
4.2 AttributeAccessor
beanDefinition.setAttribute(key, value);
這個屬性是附加屬性,並不會影響我們 Spring Bean 的實例化,初始化階段。
public class BeanConfigurationMetadataDemo {
public static void main(String[] args) {
// BeanDefinition 定義
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addPropertyValue("name", "xwf");
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// 附加屬性(不影響 Bean 實例化、屬性賦值)
beanDefinition.setAttribute("name", "小仙");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition("user", beanDefinition);
User user = beanFactory.getBean("user", User.class);
Object name = beanDefinition.getAttribute("name");
System.out.println(name);
System.out.println(user);
}
}
執行結果:
小仙
User{beanName='user', id=null, name='xwf', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
可以看到從 beanDefinition 獲取 name 屬性確實是 小仙
,但是 user 對象的 name 並沒有改變。
我們可以通過 postProcessAfterInitialization 在初始化之後利用 beanDefinition 的 attribute 信息來自己進行替換。
public class BeanConfigurationMetadataDemo {
public static void main(String[] args) {
// BeanDefinition 定義
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
beanDefinitionBuilder.addPropertyValue("name", "xwf");
AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// 附加屬性(不影響 Bean 實例化、屬性賦值)
beanDefinition.setAttribute("name", "小仙");
//當前 BeanDefinition 來自於何方(輔助作用)
beanDefinition.setSource(BeanConfigurationMetadataDemo.class);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (ObjectUtils.nullSafeEquals("user", beanName) && User.class.equals(bean.getClass())) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
if (BeanConfigurationMetadataDemo.class.equals(bd.getSource())) {
String name = String.valueOf(bd.getAttribute("name"));
User user = (User) bean;
user.setName(name);
return user;
}
}
return bean;
}
});
beanFactory.registerBeanDefinition("user", beanDefinition);
User user = beanFactory.getBean("user", User.class);
Object name = beanDefinition.getAttribute("name");
System.out.println(name);
System.out.println(user);
}
}
執行結果:
小仙
User{beanName='user', id=null, name='小仙', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
4.3 BeanMetadataElement
org.springframework.beans.BeanMetadataElement
public interface BeanMetadataElement {
/**
* Return the configuration source {@code Object} for this metadata element
* (may be {@code null}).
*/
@Nullable
default Object getSource() {
return null;
}
}
可以用表示 BeanDefinition 來自於何方(輔助作用);比如上面的例子中 BeanConfigurationMetadataDemo.class.equals(bd.getSource())
用來判斷 BeanDefinition 來源,當然前提是提前設置了來源 beanDefinition.setSource(BeanConfigurationMetadataDemo.class)
。
5.Spring 容器配置元信息
Spring XML 配置元信息 - beans 元素相關
beans 元素屬性 | 默認值 | 使用場景 |
---|---|---|
profile | null(留空) | Spring Profiles 配置值 |
default-lazy-init | default | 當 outter beans “default-lazy-init” 屬性存在是,繼承該值,否則爲"false" |
default-merge | default | 當 outter beans “default-merge” 屬性存在是,繼承該值,否則爲"false" |
default-autowire | default | 當 outter beans “default-autowire” 屬性存在是,繼承該值,否則爲"no" |
default-autowire-candidates | null(留空) | 默認 Spring Beans 名稱 pattern |
default-init-method | null(留空) | 默認 Spring Beans 自定義初始化方法 |
default-destroy-method | null(留空) | 默認 Spring Beans 自定義銷燬方法 |
Spring XML 配置元信息-應用上下文相關
XML 元素 | 使用場景 |
---|---|
<context:annotation-config /> | 激活 Spring 註解驅動 |
<context:annotation-scan /> | Spring @Component 以及自定義註解掃描 |
<context:load-time-weaver /> | 激活 Spring LoadTimeWeaver |
<context:mbean-export /> | 暴露 Spring Beans 作爲 JMX Beans |
<context:mbean-server /> | 將當前平臺作爲 MBeanServer |
<context:property-placeholder /> | 加載外部化配置資源作爲 Spring 屬性配置 |
<context:property-override /> | 利用外部化配置資源覆蓋 Spring 屬性值 |
相關的重要源碼
- org.springframework.beans.factory.xml.BeanDefinitionParserDelegate
- populateDefaults
6.基於 XML 文件裝載 Spring Bean 配置元信息
Spring bean 配置元信息
XML 元素 | 使用場景 |
---|---|
<beans:beans /> | 單 XML 資源下的多個 Spring Beans 配置 |
<beans:bean /> | 單個 Spring Bean 定義(BeanDefinition)配置 |
<beans:alias /> | 爲 Spring Bean 定義(BeanDefinition)映射別名 |
<beans:import /> | 加載外部 Spring XML 配置資源 |
底層實現 org.springframework.beans.factory.xml.XmlBeanDefinitionReader
7.基於 Properties 文件裝載 Spring Bean 配置元信息
Spring bean 配置元信息
Properites 屬性名 | 使用場景 |
---|---|
(class) | Bean 類全程限定名 |
(abstract) | 是否爲抽象的 BeanDefinition |
(parent) | 指定 parent BeanDefinition |
(lazy-init) | 是否延遲初始化化 |
(ref) | 引用其他 Bean 的名稱 |
(scope) | 設置 Bean 作用域 |
${n} | n 表示第 n+1 個構造器參數 |
底層實現 org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
8.基於 Java 註解裝載 Spring Bean 配置元信息
Spring 模式註解
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@Repository | 數據倉庫模式註解 | 2.0 |
@Component | 通用組件模式註解 | 2.5 |
@Service | 服務模式註解 | 2.5 |
@Controller | Web 控制器模式註解 | 2.5 |
@Configuration | 配置類模式註解 | 3.0 |
Spring Bean 依賴注入註解
註解 | 類型 | 場景說明 | 起始版本 |
---|---|---|---|
@Autowired | Spring 註解 | Bean 依賴注入 | 2.5 |
@Qualifier | Spring 註解 | 限定注入 | 2.5 |
@Resource | Java 註解 | 類似 @Autowired | 2.5 |
@Inject | Java 註解 | 類似 @Autowired | 2.5 |
Spring Bean 條件裝配註解
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@Profile | 配置化條件裝配 | 3.1 |
@Conditional | 編程條件裝配 | 4.0 |
Spring Bean 生命週期回調註解
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@PostConstruct | 代替 XML 元素 或者 InitializingBean | 2.5 |
@PreDestroy | 代替 XML 元素 或者 DisposableBean | 2.5 |
9.Spring Bean 配置元信息底層實現
Spring BeanDefintion 解析與註冊
實現場景 | 實現類 | 起始版本 |
---|---|---|
XML 資源 | XmlBeanDefinitionReader | 1.0 |
Properties 資源 | PropertiesBeanDefinitionReader | 1.0 |
java 註解 | AnnotatedBeanDefinitionReader | 3.0 |
9.1 Spring XML 資源 BeanDefinition 解析與註冊
核心 API - XMLBeanDefintionReader
- 資源 - Resource
- 底層- BeanDefintionDocumentReader
- XML 解析 - Java DOM Level 3 API
- BeanDefintion 解析 - BeanDefinitionParserDelegate
- BeanDefintion 註冊 - BeanDefinitionRegistry
9.2 Spring Properties 資源 BeanDefintion 解析與註冊
- 核心 API - PropertiesBeanDefinitionReader
- 資源
- 字節流 - Resource
- 字符流 - EncodedResource
- 底層
- 存儲 - java.util.Properties
- BeanDefinition 解析 - API 內部實現
- BeanDefinition 註冊 - BeanDefinitionRegistry
- 資源
9.3 Spring Java 註冊 BeanDefinition 解析與註冊
- 核心 API - AnnotatedBeanDefinitionReader
- 資源
- 類對象 - java.lang.Class
- 底層
- 條件評估 - ConditionEvaluator
- Bean 範圍解析 - ScopeMetadataResolver
- BeanDefinition 解析 - 內部 API 實現
- BeanDefinition 處理 - AnnotationConfigUtils#processCommonDefinitionAnnotations
- BeanDefintion 註冊 - BeanDefinitionRegistry
- 資源
10.基於 XML 文件裝載 Spring IOC 容器配置元信息
Spring IoC 容器相關 XML 配置
命名空間 | 所屬模塊 | Schema 資源 URL |
---|---|---|
beans | spring-beans | https://www.springframework.org/schema/beans/spring-beans.xsd |
context | spring-context | https://www.springframework.org/schema/context/spring-context.xsd |
aop | spring-aop | https://www.springframework.org/schema/aop/spring-aop.xsd |
tx | spring-tx | https://www.springframework.org/schema/tx/spring-tx.xsd |
util | spring-beans | https://www.springframework.org/schema/util/spring-util.xsd |
tool | spring-beans | https://www.springframework.org/schema/tool/spring-tool.xsd |
11.基於 Java 註解裝載 Spring IOC 容器配置元信息
11.1 Spring IoC 容器裝配註解
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@ImportResource | 替換 XML 元素 | 3.0 |
@Import | 導入 Configuration Class | 3.0 |
@ComponentScan | 掃描指定 package | 3.1 |
示例
/**
* 基於 Java 註解裝載 Spring IOC 容器配置元信息
*/
//將當前類作爲 Configuration Class
@ImportResource("classpath:/META-INF/dependency-lookup-context.xml")
@Import(User.class)
public class AnnotatedSpringIoCContainerMetaConfigurationDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 註冊當前類作爲 Configuration Class
context.register(AnnotatedSpringIoCContainerMetaConfigurationDemo.class);
// 啓動
context.refresh();
Map<String, User> beansOfType = context.getBeansOfType(User.class);
printfEach(beansOfType);
// 關閉
context.close();
}
public static void printfEach(Map<String, User> map) {
for (Map.Entry<String, User> entry : map.entrySet()) {
System.out.printf("User Bean name : %s , content : %s \n", entry.getKey(), entry.getValue());
}
}
}
執行結果:
user用戶對象初始化...
superUser用戶對象初始化...
User Bean name : com.huajie.thinking.in.spring.ioc.overview.domain.User , content : User{beanName='com.huajie.thinking.in.spring.ioc.overview.domain.User', id=null, name='null', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
User Bean name : user , content : User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}
User Bean name : superUser , content : SuperUser{address='wuhan'}User{beanName='superUser', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}
superUser用戶對象銷燬...
user用戶對象銷燬...
com.huajie.thinking.in.spring.ioc.overview.domain.User用戶對象銷燬...
11.2 Spring IoC 配置屬性註解
Spring 註解 | 場景說明 | 其實版本 |
---|---|---|
@PropertySource | 配置屬性抽象 PropertySource 註解 | 3.1 |
@PropertySources | @PropertySource 集合註解 | 4.0 |
示例
/**
* 基於 Java 註解裝載 Spring IOC 容器配置元信息
*/
//將當前類作爲 Configuration Class
@ImportResource("classpath:/META-INF/dependency-lookup-context.xml")
@Import(User.class)
@PropertySource(value = "classpath:/META-INF/user-bean-definitions.properties", encoding = "gbk")
@PropertySource(value = "classpath:/META-INF/user-bean-definitions.properties", encoding = "gbk")//可以寫多個 jdk8 @Repeatable
public class AnnotatedSpringIoCContainerMetaConfigurationDemo {
@Bean
public User configuredUser(@Value(value = "${usr.name}") String name) {
return User.createUser(name);
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 註冊當前類作爲 Configuration Class
context.register(AnnotatedSpringIoCContainerMetaConfigurationDemo.class);
// 啓動
context.refresh();
Map<String, User> beansOfType = context.getBeansOfType(User.class);
printfEach(beansOfType);
// 關閉
context.close();
}
public static void printfEach(Map<String, User> map) {
for (Map.Entry<String, User> entry : map.entrySet()) {
System.out.printf("User Bean name : %s , content : %s \n", entry.getKey(), entry.getValue());
}
}
}
執行結果:
User Bean name : com.huajie.thinking.in.spring.ioc.overview.domain.User , content : User{beanName='com.huajie.thinking.in.spring.ioc.overview.domain.User', id=null, name='null', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
User Bean name : configuredUser , content : User{beanName='configuredUser', id=null, name='小仙', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
User Bean name : user , content : User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}
User Bean name : superUser , content : SuperUser{address='wuhan'}User{beanName='superUser', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}
12.基於 Extensible XML authoring 擴展 Spring XML 元素
Spring XML 擴展
- 編寫 XML Schema 文件:定義 XML 結構
- 自定義 NamespaceHandler 實現:命名空間綁定
- 自定義 BeanDefinitionParser 實現:XML 元素與 BeanDefinition 解析
- 註冊 XML 擴展:命名空間與 XML Schema 映射
12.1 編寫 XML Schema 文件
在 resource 目錄下面建相同的包路徑 com.huajie.thinking.in.spring.configuration.metadata
新建 users.xsd
文件
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://com.huajie/schema/users"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://com.huajie/schema/users">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<!-- 定義 User 類型(定義複雜類型) -->
<xsd:complexType name="User">
<xsd:attribute name="id" type="xsd:integer" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="city" type="City"/>
</xsd:complexType>
<!-- 定義 City 類型(簡單類型,枚舉) -->
<xsd:simpleType name="City">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="WUHAN"/>
<xsd:enumeration value="BEIJING"/>
<xsd:enumeration value="SHANGHAI"/>
</xsd:restriction>
</xsd:simpleType>
<!-- 定義 user 元素 -->
<xsd:element name="user" type="User"/>
</xsd:schema>
新建 Spring xml 配置文件 users-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:users="http://com.huajie/schema/users"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://com.huajie/schema/users
http://com.huajie/schema/users.xsd">
<users:user id="1" name="小仙" city="WUHAN"/>
</beans>
12.2 自定義 NamespaceHandler 實現 & 自定義 BeanDefinitionParser 實現
新建 UsersNamespaceHandler
public class UsersNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UsersBeanDefinitionParser());
}
private static class UsersBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
setPropertyValue("id", element, builder);
setPropertyValue("name", element, builder);
setPropertyValue("city", element, builder);
}
private void setPropertyValue(String attributeName, Element element, BeanDefinitionBuilder builder) {
String value = element.getAttribute(attributeName);
if (StringUtils.hasText(value)) {
builder.addPropertyValue(attributeName, value);
}
}
}
}
在 resource/META-INF 目錄下面新建 spring.handlers 文件
http\://com.huajie/schema/users=com.huajie.thinking.in.spring.configuration.metadata.UsersNamespaceHandler
12.3 註冊 XML 擴展
在 resource/META-INF 目錄下面新建 spring.schemas 文件
http\://com.huajie/schema/users.xsd=com/huajie/thinking/in/spring/configuration/metadata/users.xsd
12.4 示例
/**
* Spring xml 元素擴展示例
*/
public class ExtensibleXmlAuthoringDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
String location = "classpath:/META-INF/users-context.xml";
xmlBeanDefinitionReader.loadBeanDefinitions(location);
User bean = beanFactory.getBean(User.class);
System.out.println(bean);
}
}
執行結果:
User{beanName='1', id=1, name='小仙', age=null, configFileReource=null, city=WUHAN, cities=null, lifeCities=null}
13.Extensible XML authoring 擴展原理
觸發時機在啓動應用上下文時,Application.refresh() 方法
- org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
- AbstractRefreshableApplicationContext#refreshBeanFactory
- AbstractXmlApplicationContext#loadBeanDefinitions
- AbstractBeanDefinitionReader#loadBeanDefinitions
- BeanDefinitionParserDelegate#parseCustomElement
- AbstractBeanDefinitionReader#loadBeanDefinitions
- AbstractXmlApplicationContext#loadBeanDefinitions
- AbstractRefreshableApplicationContext#refreshBeanFactory
BeanDefinitionParserDelegate#parseCustomElement 核心流程
- 獲取 namespace
- 通過 namespace 解析 NamespaceHandler
- 構造 ParserContext
- 解析元素,獲取 BeanDefinition
13.1 源碼調試
使用 12.基於 Extensible XML authoring 擴展 Spring XML 元素 小節中的示例
調試我們將斷點打在 BeanDefinitionParserDelegate#parseCustomElement 1383 行
1.獲取 namespace
2.通過 namespace 解析 NamespaceHandler 獲取到我們定義的 handler
3.構造 ParserContext
4.調用我們 handler#parse 方法,將 element 解析成 BeanDefinition
14.基於 Properties 文件裝載外部化配置
註解驅動
- org.springframework.context.annotation.PropertySource
- org.springframework.context.annotation.PropertySources
API 編程
- org.springframework.core.env.PropertySource
- org.springframework.core.env.PropertySources
14.1 示例
在本章 11.2 Spring IoC 配置屬性註解 中已經有相關的示例
我們對示例做一定的改造,在外部化配置加載之前,新建一個 PropertySource 去替換其中的 usr.name 屬性;PropertySources 有一定的順序,先添加的 PropertySource 可以覆蓋後添加的。
/**
* 外部化配置示例
*/
@PropertySource(value = "classpath:/META-INF/user-bean-definitions.properties", encoding = "gbk")
public class PropertySourceDemo {
@Bean
public User user(@Value(value = "${usr.name}") String name) {
return User.createUser(name);
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 擴展 Environment 中的 propertySources
// 這個操作必須在 context.refresh() 之前完成
Map<String,Object> propertySource = new HashMap<>();
propertySource.put("usr.name","new-小仙");
MapPropertySource ms = new MapPropertySource("first-property-source",propertySource);
context.getEnvironment().getPropertySources().addFirst(ms);
// 註冊當前類作爲 Configuration Class
context.register(PropertySourceDemo.class);
// 啓動
context.refresh();
Map<String, User> beansOfType = context.getBeansOfType(User.class);
printfEach(beansOfType);
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
System.out.println(propertySources);
// 關閉
context.close();
}
public static void printfEach(Map<String, User> map) {
for (Map.Entry<String, User> entry : map.entrySet()) {
System.out.printf("User Bean name : %s , content : %s \n", entry.getKey(), entry.getValue());
}
}
}
執行結果:
User Bean name : user , content : User{beanName='user', id=null, name='new-小仙', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
[MapPropertySource {name='first-property-source'}, PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}, ResourcePropertySource {name='class path resource [META-INF/user-bean-definitions.properties]'}]
可以看到 user 對象的 name = ‘new-小仙’
MapPropertySource 順序:
1.first-property-source
我們自定義的 PropertySource
2.systemEnvironment
Java 系統環境變量
3.通過 properties 文件加載的 PropertySource
15.基於 YAML 文件裝載外部化配置
API 編程
- org.springframework.beans.factory.config.YamlProcessor
- org.springframework.beans.factory.config.YamlMapFactoryBean
- org.springframework.beans.factory.config.YamlPropertiesFactoryBean
15.1 基於 XML 示例
新建 user.yaml 文件;user.name
和 Java 系統環境變量相同,所以我們用 usr 表示
usr:
id: 1
name: 小仙-yaml
新建 yaml-property-source-context.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="yamlMap" class="org.springframework.beans.factory.config.YamlMapFactoryBean">
<property name="resources" value="classpath:/META-INF/user.yaml"/>
</bean>
</beans>
調用示例
/**
* 基於 xml 的 yaml 外部化配置示例
*/
public class XmlBasedYamlPropertySourceDemo {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
String location = "classpath:/META-INF/yaml-property-source-context.xml";
xmlBeanDefinitionReader.loadBeanDefinitions(location);
Map<String, Object> users = beanFactory.getBean("yamlMap", Map.class);
System.out.println(users);
}
}
執行結果:
{usr={id=1, name=小仙-yaml}}
15.2 基於 Java 註解示例
新建 YamlPropertySourceFactory
public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
yamlPropertiesFactoryBean.setResources(resource.getResource());
Properties properties = yamlPropertiesFactoryBean.getObject();
return new PropertiesPropertySource(name,properties);
}
}
示例
/**
* 基於 Java 註解的 yaml 外部化配置示例
*/
@PropertySource(name="yamlPropertySource",value = "classpath:/META-INF/user.yaml",factory = YamlPropertySourceFactory.class)
public class AnnotatedYamlPropertySourceDemo {
@Bean
public User user(@Value(value = "${usr.name}") String name) {
return User.createUser(name);
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 註冊當前類作爲 Configuration Class
context.register(AnnotatedYamlPropertySourceDemo.class);
// 啓動
context.refresh();
User user = context.getBean("user", User.class);
System.out.println(user);
// 關閉
context.close();
}
}
執行結果:
User{beanName='user', id=null, name='小仙-yaml', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
16.面試題
16.1 Spring 內建 XML 有哪些?
命名空間 | 所屬模塊 | Schema 資源 URL |
---|---|---|
beans | spring-beans | https://www.springframework.org/schema/beans/spring-beans.xsd |
context | spring-context | https://www.springframework.org/schema/context/spring-context.xsd |
aop | spring-aop | https://www.springframework.org/schema/aop/spring-aop.xsd |
tx | spring-tx | https://www.springframework.org/schema/tx/spring-tx.xsd |
util | spring-beans | https://www.springframework.org/schema/util/spring-util.xsd |
tool | spring-beans | https://www.springframework.org/schema/tool/spring-tool.xsd |
16.2 Spring 配置元信息具體有哪些?
- Bean 配置元信息:通過媒介(如 XML、Properties等),解析 BeanDefinition
- IoC 容器配置元信息:通過媒介(如 XML、Properties等),控制 IoC 容器行爲,比如註解驅動,AOP等
- 外部化配置:通過資源抽象(如 Properties、YAML等),控制 PropertySource
- Spring Profile:通過外部化配置,提供條件分支流程、
16.3 Extensible XML authoring 的缺點?
- 高複雜度:開發人員需要輸血 XML Schema,spring.handlers,spring.schemas 以及 Spring API。
- 嵌套元素支持比較弱:通常需要使用方法遞歸或者其嵌套解析方式處理嵌套子元素
- XML 處理性能較差:Spring XML 基於 DOM Level 3 API 實現,該 API 便於理解,然而性能較差。
- XML 框架移植性差:很難適配高性能和便利性的 XML 框架,如 JAXB。
17.參考
- 極客時間-小馬哥《小馬哥講Spring核心編程思想》