文章目录
- 夯实Spring系列|第十五章:Spring 配置元信息
- 本章说明
- 1.项目环境
- 2.Spring 配置元信息
- 3.Spring Bean 配置元信息
- 4.Spring Bean 属性元信息
- 4.1 PropertyValues
- 4.2 AttributeAccessor
- 4.3 BeanMetadataElement
- 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 容器配置元信息
- 11.1 Spring IoC 容器装配注解
- 示例
- 11.2 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 扩展原理
- 13.1 源码调试
- 14.基于 Properties 文件装载外部化配置
- 14.1 示例
- 15.基于 YAML 文件装载外部化配置
- 15.1 基于 XML 示例
- 15.2 基于 Java 注解示例
- 16.面试题
- 16.1 Spring 内建 XML 有哪些?
- 16.2 Spring 配置元信息具体有哪些?
- 16.3 Extensible XML authoring 的缺点?
- 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
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.参考