曹工说Spring Boot源码(9)-- Spring解析xml文件,到底从中得到了什么(context命名空间上)
写在前面的话
相关背景及资源:
工程代码地址 思维导图地址
工程结构图:
概要
先给大家看看spring支持的xml配置,我列了个表格如下:
namespace | element |
---|---|
util | constant、property-path、list、set、map、properties |
context | property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export、mbean-server |
beans | import、bean、alias |
task | annotation-driven、scheduler、scheduled-tasks、executor |
cache | advice、annotation-driven |
aop | config、scoped-proxy、aspectj-autoproxy |
我题目的意思是,spring在解析每个不同的xml元素时,其实是有共性的。所有这些元素的解析器,都实现了BeanDefinitionParser
。这个接口只有一个方法,作用就是解析元素时,根据元素的配置,来收集beanDefinition
,正所谓:条条大道通罗马,各种xml配置元素,各种注解配置,就是那些大道,罗马是什么?
就是beanDefinition
。
从第一篇到现在,已经第9篇了,我们还在讲bean definition
,其实就是因为,只有深刻地理解了它,后面才能更方便地理解spring boot,理解configuration注解,理解enable,理解自动装配。
好了,切入本篇,本篇要讲解的xml元素是context命名空间里的。
context:property-placeholder
用法
@Data
public class TestPropertiesVO {
private String name;
}
#application.properties
name: Phil
测试代码:
package org.springframework.contextnamespace;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.MyFastJson;
import java.util.List;
import java.util.Map;
@Slf4j
public class TestPropertyPlaceholder {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"classpath:context-namespace-test-property-holder.xml"},false);
context.refresh();
Map map = context.getDefaultListableBeanFactory().getAllSingletonObjectMap();
log.info("singletons:{}", JSONObject.toJSONString(map));
List list =
context.getBeanFactory().getBeanDefinitionList();
MyFastJson.printJsonStringForBeanDefinitionList(list);
// 获取该bean,打印
Object bean = context.getBean(TestPropertiesVO.class);
System.out.println("bean:" + bean);
}
}
输出如下:
bean:TestPropertiesVO(name=Phil)
如果我们修改xml:
//注释之,看看会怎样
输出如下:
bean:TestPropertiesVO(name=${name})
可以看到,这样子呢,就没法解析到properties中的值了。
等价用法
<?xml version="1.0" encoding="UTF-8"?>
// 这个配置方式,和上面那个,效果其实是一样的;上面那个,是对下边这种的封装
classpath:application.properties
元素解析
我们切入到org.springframework.context.config.ContextNamespaceHandler
,查找下该元素的解析器。
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}
我们可以看到,本元素的解析器是:PropertyPlaceholderBeanDefinitionParser
。
先看看类继承结构:
大家注意第三层,类名里,有Single字样,说明了它是单身狗?不是。说明这个xml元素解析器,最终只得到一个bean definition。
第四层的AbstractPropertyLoadingBeanDefinitionParser
,就是提供一个抽象类,提取一些https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-xml-demo/src/main/java/org/springframework/contextnamespace
由于context命名空间都是些大人物,所以本篇主要是先给大家热身,下一讲,我们讲讲这里面的:
annotation-config、component-scan
我简单看了两眼,还挺有意思,欢迎大家和我一起学习。