记录一下spring自定义标签理解过程。
关键的三个点
- spring.handlers和spring.schemas配置文件解析。
- user.xsd对象解析id和name作为spring的基础数据会被解析
- 了解一下NamespaceHandlerSupport的parser方法调用逻辑,xml对象如何被解析。
文件夹目录
示例执行结果
在这里插入图片描述
主题目录
***point 1/3> MATE-INF目录写死
***point 2/3> 调用DefaultNamespaceHandlerResolver.init();
***point 3/3> AbstractSingleBeanDefinitionParser.doParse();
扩展解析
***point 0/0>解析xml配置文件id和其父类属性
NamespaceHandlerSupport
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取元素的解析器
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
此处代码解析xml的id和name属性
最后都是存储到spring容器的beanDefinitionMap和beanDefinition内部中解析对象可能是xml,注解比如commonpent注解,测试时根据实际情况构造。
***point 1/3> MATE-INF目录写死
DefaultNamespaceHandlerResolver字段DEFAULT_HANDLER_MAPPINGS_LOCATION = “META-INF/spring.handlers”;
对应写死的配置文件路径。此处代码解析spring.handlers配置文件
PluggableSchemaResolver字段DEFAULT_SCHEMA_MAPPINGS_LOCATION = “META-INF/spring.schemas”;
对应写死的配置文件路径。此处代码解析spring.schemas配置文件
spring自定义标签的代码
启动类
package com.mashibing.selftag2;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSelfTag2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("selftag2.xml");
User2 user2=(User2)context.getBean("testbean2");
System.out.println("username:"+user2.getUsername()+" "+"email:"+user2.getEmail());
}
}
标签类,转换对象
package com.mashibing.selftag2;
public class User2 {
private String username;
private String email;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
标签解析器
package com.mashibing.selftag2;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
public class User2BeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
// 返回属性值所对应的对象
@Override
protected Class<?> getBeanClass(Element element) {
return User2.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
// 获取标签具体的属性值
String userName = element.getAttribute("userName");
String email = element.getAttribute("email");
String password = element.getAttribute("password");
if(StringUtils.hasText(userName)){
builder.addPropertyValue("username",userName);
}
if (StringUtils.hasText(email)){
builder.addPropertyValue("email",email);
}
if (StringUtils.hasText(password)){
builder.addPropertyValue("password",password);
}
}
}
标签命名句柄
package com.mashibing.selftag2;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class User2NamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user",new User2BeanDefinitionParser());
}
}
配置文件
spring.handlers
http\://www.nanami.com/schema/user=com.mashibing.selftag2.User2NamespaceHandler
spring.schemas
http\://www.nanami.com/schema/user.xsd=META-INF/user.xsd
user.xsd
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.nanami.com/schema/user"
xmlns:tns="http://www.nanami.com/schema/user"
elementFormDefault="qualified">
<element name="user">
<complexType>
<attribute name ="id" type = "string"/>
<attribute name ="userName" type = "string"/>
<attribute name ="email" type = "string"/>
<attribute name ="password" type="string"/>
</complexType>
</element>
</schema>
selftag2.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"
xmlns:a2="http://www.nanami.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.nanami.com/schema/user http://www.nanami.com/schema/user.xsd
">
<a2:user id = "testbean2" userName = "test_lee_testbean2" email = "test_email@qq.com"/>
</beans>
关键源码
1.xml对象解析代码。
2.parse方法解析对象
此逻辑分为两块,一块是调用init方法User2NamespaceHandler继承自NamespaceHandlerSupport
另外一块是调用doParse方法,映射User2BeanDefinitionParser继承自AbstractSingleBeanDefinitionParser
备注:路径描述
AbstractApplicationContext的refersh作用基础方法
public void refresh() throws BeansException, IllegalStateException {
//Tell the subclass to refresh the internal bean factory.
// 创建容器对象:DefaultListableBeanFactory
// 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
}
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录在当前实体的属性中
refreshBeanFactory();
}
AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException {
// 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析
loadBeanDefinitions(beanFactory);
}
AbstractXmlApplicationContext
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 开始完成beanDefinition的加载
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
reader.loadBeanDefinitions(configLocations);
}
AbstractBeanDefinitionReader
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
count += loadBeanDefinitions(location);
}
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
int count = loadBeanDefinitions(resources);
}
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
count += loadBeanDefinitions(resource);
}
XmlBeanDefinitionReader
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
// 逻辑处理的核心步骤
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
int count = registerBeanDefinitions(doc, resource);
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 完成具体的解析过程
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
}
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
DefaultBeanDefinitionDocumentReader
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
DefaultBeanDefinitionDocumentReader
protected void doRegisterBeanDefinitions(Element root) {
parseBeanDefinitions(root, this.delegate);
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
delegate.parseCustomElement(ele);
}
BeanDefinitionParserDelegate***point***
对应自定义AbstractSingleBeanDefinitionParser和NamespaceHandlerSupport的实现类调用。
public BeanDefinition parseCustomElement(Element ele) {
***point 2/3> 调用DefaultNamespaceHandlerResolver.init();
// 根据命名空间找到对应的NamespaceHandlerspring
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
***point 3/3> AbstractSingleBeanDefinitionParser.doParse();
// 调用自定义的NamespaceHandler进行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
NamespaceHandlerSupport
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 获取元素的解析器,
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
AbstractBeanDefinitionParser
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
}
AbstractSingleBeanDefinitionParser
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
doParse(element, parserContext, builder);
}
关联两个实现类的调用
使用ClassUtils.forName方法实例化类路径,实例化以后继续深入,会调用init()和doParser()方法。
DefaultNamespaceHandlerResolver
namespaceHandler.init();
// 调用自定义的namespaceHandler的初始化方法
***point 2/3> 调用DefaultNamespaceHandlerResolver.init();
AbstractSingleBeanDefinitionParser
// 调用子类重写的doParse方法进行解析
doParse(element, parserContext, builder);
备注:BeanDefinitionBuilder对象动态映射值,通过key和value方式
***point 3/3> AbstractSingleBeanDefinitionParser.doParse();
gradle运行时的一些逻辑
资源文件:https://download.youkuaiyun.com/download/weixin_50750933/74792262
断点环境:https://download.youkuaiyun.com/download/weixin_50750933/74903063
感受
想起和最开始学习ssh框架,xml配置文件写entity对象。当时也看了一下spring的对接dom对象解析。也是写死的内容。
从refresh方法到具体实现方法,仅仅只是一个调用。就走了几十个方法,N个类。只能先用大量时间先熟悉了。