java 请问下SpringMVC中Spring是怎么去解析XML?
spring使用了jdk自带的jaxp解析技术,没有使用dom4j,所以spring工程里面也并没有依赖dom4j的jar。你可以看看XmlBeanDefinitionReader中的documentLoader属性,使用了DefaultDocumentLoader类来初始化的,DefaultDocumentLoader中的引用(import)都是JDK自带的类,DefaultDocumentLoader是spring用来解析xml的类。
Springboot自定义xml文件解析
有时候,要通过自定义XML配置文件来实现一些特定的功能。这里通过例子来说明。
首先,看部分spring加载bean文件的源码:
spring-beans-5.0.6.RELEASE.jar!/org/springframework/beans/factory/xml/PluggableSchemaResolver.class :
spring-beans-5.0.6.RELEASE.jar!/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.class :
可以看出,spring在加载xml文件的时候,会默认读取配置文件 META-INF/spring.schemas 和 META-INF/spring.handlers 。这样,我们就可以在这两个文件添加我们自定义的xml文件格式和xml文件解析处理器。
新建一个Springboot工程,pom如下。
SelfDefineXmlTrial/pom.xml :
然后,新建一个用于测试controller。
com.lfqy.springboot.selfdefxml.controller.SelfDefXmlController :
最后,创建一个Springboot的启动类。
com.lfqy.springboot.selfdefxml.SelfDefXmlApplication :
运行启动之后,浏览器访问 效果如下:
修改前面提到的配置文件 META-INF/spring.schemas 、 META-INF/spring.handlers ,添加xml格式说明。
META-INF/spring.schemas :
META-INF/spring.handlers :
添加xml格式说明配置文件。
META-INF/selfdef.xsd :
添加自定义xml格式处理器类。
com.lfqy.springboot.selfdefxml.selxmlparse.UserNamespaceHandler :
新增xml格式解析类。
com.lfqy.springboot.selfdefxml.selxmlparse.UserBeanDefinitionParser :
新增自定义xml对应的bean类。
com.lfqy.springboot.selfdefxml.beans.User :
添加自定义xml配置文件读取的相关逻辑。
com.lfqy.springboot.selfdefxml.SelfDefXmlApplication :
到这里,编码就完成了,工程的目录结构如下。
运行之后,控制台输出如下:
这里,通过实现一个启动时自动初始化的一个servlet来实现。
com.lfqy.springboot.selfdefxml.servlet.StartupServlet :
在启动时加载servlet,为了方便区分,这里新写一个启动类。
com.lfqy.springboot.selfdefxml.SelfDefXmlLoadOnStartupApplication
到这里,编码已经完成,工程的目录结构如下:
运行之后,控制台输出如下:
【Spring源码配置文件解析】2. xml注入配置信息 & @Value
这样xml中配置的bean的属性就会被注入配置文件里面对应的值
首先xml中的bean会在扫描的过程中封装成BeanDefinition对象,property标签会被弄成一个ProprotyValue的集合放在BeanDefinition的ProprotyValues变量中,所以在xml解析完成之后的BeanDefinition的ProprotyValues变量是这样的
上节PropertySourcesPlaceholderConfigurer这个类收集了environment配置信息和本地配置信息,并把它放在了PropertySourcesPropertyResolver的propertySources属性中
最后创建了一个StringValueResolver对象会调用PropertySourcesPropertyResolver来处理配置信息的替换
接下来就是取出所有的BeanDefinition,看看beanDefinition中的属性中是否有${}表达式,有的话就替换
很多的属性都可以用${}来引用配置信息
重点看看属性是如何被替换的
在BeanDefinitionVisitor.resolveValue方法中,String类型的走这
最终会调到PropertySourcesPlaceholderConfigurer创建的StringValueResolver匿名对象的实现方法中
这个匿名对象实现的方法又会调用PropertySourcesPropertyResolver来替换值,前面有提到所有的配置信息都在PropertySourcesPropertyResolver.propertySources中,那么接下来的工作就是从这个容器中找到对应的配置信息的key所对应的value
这里在入参时会创建一个PlaceholderResolver的匿名对象,实现的resolvePlaceholder方法将会调用PropertySourcesPropertyResolver.getPropertyAsRawString()
最后返回了被替换成对应配置信息的值
这里就会调用前面创建的匿名对象的实现方法,方法体重会调用调用PropertySourcesPropertyResolver.getPropertyAsRawString(),去用key获取对应的配置信息
PropertySourcesPropertyResolver.getPropertyAsRawString()
PropertiesPropertySource对象内部有name,和source,source是一个泛型,当前类型为Properties,PropertiesPropertySource需要实现getProperty方法,其实就是从source中获取属性值
最后调到了Properties类的get方法,返回value
对每个beanDefination都这样操作过一遍
注意这个StringValueResolver的resolveStringValue会调用PropertySourcesPropertyResolver的方法来处理配置信息的替换,PropertySourcesPropertyResolver持有了所有的配置信息。 那么后面@Value的解析也将StringValueResolver来完成
@Value的解析工作是在Bean实例化后,属性注入的时候从配置文件找出并设置进去的
populateBean方法
只有string类型的才能@Value注解,才需要处理
又是这个容器,之前PropertySourcesPlaceholderConfigurer的doProcessProperties放进去的StringValueResolver
最后又会回到这个地方解析并注入值,和xml方式获取配置信息是一样的
找到对应的配置信息之后,反射设置这个属性的值
如何使用spring解析xml文件
Spring框架从2.0版本开始,提供了基于Schema风格的XML扩展机制,允许开发者扩展最基本的spring配置文件(一般是classpath下的spring.xml)。试想一下,如果我们直接在spring.xml中加入一个自定义标签mytag id="aty"/matag,会发生什么呢?spring框架启动的时候会报错,因为spring根本不认识我们自定义的mytag,这样对spring.xml的校验就会失败,最终结果就是框架不能启动。有什么方法,能够让spring认识并加载解析我们自定义的mytag呢?这就是spring提供的xml扩展机制。我们可以在spring.xml中加入自己的标签,之后spring会帮我们解析并纳入自己的管理范围内,这也就是说我们扩展了spring的功能。
现在我们来看下怎么实现这个功能,可以参考spring帮助文档中的extensible-xml.html。我们知道如果在需要在spring.xml中配置数据源,需要进行如下的配置:
bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
property name="driverClassName" value="com.mysql.jdbc.Driver" /
property name="url" value="jdbc:mysql://localhost:3309/sampledb" /
property name="username" value="root" /
property name="password" value="1234" /
/bean