您的位置:首页 > 编程语言 > Java开发

Spring源码解析-自定义标签的解析

2017-08-15 16:32 465 查看
首先来讲一下这个自定义标签怎么用:

应用

①创建一个需要拓展的组件,这里我们创建一个pojo类

public class User {
private String userName;
private String email;

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}
}


②定义一个XSD结构描述文件,命名为Spring-test.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.corefuture.cn/schema/user"
elementFormDefault="qualified">
<element name="user">
<complexType>
<attribute name="id" type="string"/>
<attribute name="userName" type="string"/>
<attribute name="email" type="string"/>
</complexType>
</element>
</schema>


③创建一个类,实现BeanDefinitionPaser接口,用来解析我们自己定义的标签

package parser;

import bean.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
* Created by WHZ on 2017/8/15 0015.
*/
public class UserBeanDefinitionPaser extends AbstractSingleBeanDefinitionParser{
@Override
protected Class getBeanClass(Element element){
return User.class;
}

@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
//获取属性userName
String userName = element.getAttribute("userName");
//获取属性email
String email = element.getAttribute("email");
//添加到BeanDefinitionBuilder
if(StringUtils.hasText(userName)){
builder.addPropertyValue("userName",userName);
}
if(StringUtils.hasText(email)){
builder.addPropertyValue("email",email);
}
}
}


④创建一个继承了NamespaceHandlerSupport的类,用来将之前的解析器注册到spring容器中

package handler;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import parser.UserBeanDefinitionPaser;

/**
* Created by Administrator on 2017/8/15 0015.
*/
public class MyNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("user",new UserBeanDefinitionPaser());
}
}


⑤在工程文件的META-INF下添加两个文件,如果没有META-INF就自己创建,创建一个spring.hadlers

http\://www.corefuture.cn/schema/user=handler.MyNamespaceHandler


创建一个spring.schemas

http\://www.corefuture.cn/schema/user.xsd=META-INF/Spring-test.xsd


记住这两个文件名最好要小写,因为这样idea这个编译环境能识别这两个文件,然后可以检查出文件内容的错误。

⑥接着就是配置spring的配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springfr
f0f6
amework.org/schema/tx"
xmlns:myname="http://www.corefuture.cn/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.corefuture.cn/schema/user http://www.corefuture.cn/schema/user.xsd"> 
<myname:user id="aa" userName="aaaa" email="aaaa"/>
</beans>


⑦测试

package client;

import bean.User;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
* Created by Administrator on 2017/8/14 0014.
*/
public class BeanFactoryTest {

@Test
public void testSimpleLoad(){
BeanFactory bf =new XmlBeanFactory(new ClassPathResource(("beanFactoryTest.xml")));
User myTestBean = (User)bf.getBean("aa");
System.out.println(myTestBean.getEmail());
}
}
结果
aaaa


接着来看对自定义标签的解析过程

解析过程

回到之前的代码

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if(delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();

for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if(node instanceof Element) {
Element ele = (Element)node;
if(delegate.isDefaultNamespace(ele)) {
//解析默认
this.parseDefaultElement(ele, delegate);
} else {
//解析自定义标签
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}

}


当然在解析spring配置文件时,SAX会通过我们在spring.schemas中定义的约束文件的位置,对标签进行校验,接下来来看一下自定义标签的解析。进入parseCustomElement方法。

public BeanDefinition parseCustomElement(Element ele) {
return this.parseCustomElement(ele, (BeanDefinition)null);
}

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//获取命名空间
String namespaceUri = this.getNamespaceURI(ele);
//根据命名空间赵铎对应的NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if(handler == null) {
this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
} else {
//解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}


我们来看一下resolve这个方法

这个是接口方法,所以需要找到实体类,在readerContext初始化时这个NamespaceHandlerResoler已经被初始化为DefaultNamespaceHandlerResolver,然后我们进入这个类的resolve方法。

public NamespaceHandler resolve(String namespaceUri) {
//获取所有已经配置的handler映射
Map<String, Object> handlerMappings = this.getHandlerMappings();
//根据命名空间找到对应的handler或者类名
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if(handlerOrClassName == null) {
return null;
} else if(handlerOrClassName instanceof NamespaceHandler) {
//如果是NamespaceHandler,说明已经调用了init方法,注册自定义的解析器,那么直接返回
return (NamespaceHandler)handlerOrClassName;
} else {
//如果是类名,说明还没初始化注册
String className = (String)handlerOrClassName;

try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if(!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
} else {
//通过反射机制实例化handler
NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
//初始化,就是调用我们自己handler类的方法,将我们自定义的解析器注册到handler中
namespaceHandler.init();
//记录缓存
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
} catch (ClassNotFoundException var7) {
throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7);
} catch (LinkageError var8) {
throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8);
}
}
}


接着我们来看之前的handler.parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
//寻找解析器并解析
return this.findParserForElement(element, parserContext).parse(element, parserContext);
}

private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
//获取元素名称也就是<myname:user>的user
String localName = parserContext.getDelegate().getLocalName(element);
//找到解析器然后返回
BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
if(parser == null) {
parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}

return parser;
}


很明显这个返回的解析器是我们之前注册进去的解析器,现在将这解析器取出来执行parse方法,我们定义的解析器没有parse方法,所以肯定在父类或者祖父类中,最后在AbstractBeanDefinitionParser中找到parse方法

public final BeanDefinition parse(Element element, ParserContext parserContext) {
//对元素进行解析 然后获取beandefiniton
AbstractBeanDefinition definition = this.parseInternal(element, parserContext);


我们进入这个方法查看,这是个模板方法,模板方法由子类实现,我们要在AbstractSingleBeanDefinitionParser寻找

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
//得到一个genericBeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = this.getParentName(element);
if(parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
//解析调用我们定义的解析器paser的getBeanClass方法
Class<?> beanClass = this.getBeanClass(element);
if(beanClass != null) {
//设置class          builder.getRawBeanDefinition().setBeanClass(beanClass);
} else {
String beanClassName = this.getBeanClassName(element);
if(beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}

builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
if(parserContext.isNested()) {
builder.setScope(parserContext.getContainingBeanDefinition().getScope());
}

if(parserContext.isDefaultLazyInit()) {
builder.setLazyInit(true);
}
//调用我们的doPase方法
this.doParse(element, parserContext, builder);
//将装载完属性的beandefinition返回
return builder.getBeanDefinition();
}


继续看原来的方法

if(definition != null && !parserContext.isNested()) {
try {
//获取id
String id = this.resolveId(element, definition, parserContext);
//在这里一定要有id,说明我们的标签一定要有id属性。否则就会报错,也就意味着beanName不能被name所代替
if(!StringUtils.hasText(id)) {
parserContext.getReaderContext().error("Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag", element);
}

String[] aliases = new String[0];
//解析name属性,当然主要还是为了获取别名
String name = element.getAttribute("name");
if(StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
//装配BeanDefinitionHolder
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
//然后注册到spring容器中,当然这个注册和默认标签bean注册是一样的
this.registerBeanDefinition(holder, parserContext.getRegistry());
//如果需要就通知监听器
if(this.shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
this.postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
} catch (BeanDefinitionStoreException var9) {
parserContext.getReaderContext().error(var9.getMessage(), element);
return null;
}
}

return definition;
}


总结

①通过命名空间,从handlermap映射中获取handler,如果取到了handler类,那么直接返回,如果没有,那返回的肯定是全类名,然后通过全类名用反射机制进行实例化,调用这个handler的init方法注册我们定义的解析器,再放回handlermap中。

②调用handler的parse方法,先找到对应的解析器,然后对元素进行解析,先解析class等一些属性,然后调用我们自己的doParse方法,最后返回承载了各种属性的beandefinition,然后得到元素的id作为beanName,然后将beandefinition,beanName,别名集合装配到beanDefinitionHolder中

③最后将beanDefinitionHolder注册到容器中,正确说是将beanDefinitionHolder中的beandefinition注册到容器中,当然还有别名和beanName之间的映射。这和之前讲过的默认标签的bean注册的过程是一样的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: