前面我们知道了spring是如何解析bean标签的子标签如 meta、lookup-method、replaced-method、constructor-arg、property以及qualifier等,我们都是一一做了简答的解读,还有一种自定义类型的标签,接下来我们来看spring对自定义标签的解析过程,首先我们回到解析默认标签的方法的起始入口:DefaultBeanDefinitionDocumentReader#processBeanDefinition这里是解析beanDefinition的入口,其中包括我们自定义标签的解析过程
- 首先是构建一个BeanDefinitionHolder的实例,在该实例不为null时去解析自定义标签
BeanDefinitionHolder为 name 和 alias 的 BeanDefinition 对象
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
若不为null
if (bdHolder != null) {
对自定义标签解析(因为我们可能对该标签有自己定义的属性)
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
在上述的代码中我们发现真正解析自定义标签的入口是:
delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
找到方法入口了,我们接下来看真正的解析的过程:
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
}
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = definitionHolder;
// Decorate based on custom attributes first.
//获取所有的属性
NamedNodeMap attributes = ele.getAttributes();
//遍历所有的属性,并寻找是否可以装饰的属性
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
// Decorate based on custom nested elements.
//获取所有的子元素
NodeList children = ele.getChildNodes();
//遍历所有的子元素,并寻找可以装饰的子元素
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
这就是解析的过程,简单的看一下过程:
- 首先是对于BeanDefinitionHolder的初始化过程
- 首先获取元素的所有的属性
BeanDefinitionHolder finalDefinition = definitionHolder;
- 接着遍历属性,并判断是否有可以装饰的属性
- 如果有,通过其decorateIfRequired()进行装饰
装饰属性的过程
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
//获取自定义的命名空间
String namespaceUri = getNamespaceURI(node);
//对于默认标签的默认标签进行过滤
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
//根据命名空间去获取对应的处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
//进行装饰
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
else if (namespaceUri.startsWith("http://www.springframework.org/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
}
else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}
这是对属性装饰的详细过程,先说完所有的说完后我们接着更详细的看
- 接着获取所有的子元素
NodeList children = ele.getChildNodes();
- 遍历所有的子元素,装饰可以装饰的子元素
装饰子元素的过程跟装饰属性的过程是一致的,接下来我们详细的介绍下自定义标签的解析过程,我们知道我们所有的操作都是从配置到资源的封装再到转换成Document的过程,随之而来的是提取document里的各种元素的过程,并展开了对提取到的元素进行解析并装饰,由于spring中的标签有默认和自定义两种,spring对其分别解析过程,首先我们看一下自定义标签的使用过程:
- 首先需要创建一个拓展的组件.
- 接着是定义一个xsd文件描述组件的内容.
- 接着是创建一个实现AbstractSingleBeanDefinitionParser 接口的类,用来解析xsd文件的定义和组件的定义.
- 定义一个Handler继承NamespaceHandlerSupport 抽象类 ,其目的是将组件注册到spring的容器中.
- 编写 spring.handlers和spring.schemas文件
接下来我们按照上述的步骤来实现自定义标签的过程
(1) 首先创建一个实体类
public class User {
private String name;
private String email;
(2) 定义xsd组件
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.jianshu.com/schema/user"
targetNamespace="http://www.jianshu.com/schema/user"
elementFormDefault="qualified">
<xsd:element name="user">
<xsd:complexType>
<xsd:attribute name="userName" type="xsd:string" />
<xsd:attribute name="email" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
(3) 创建一个实现AbstractSingleBeanDefinitionParser的类
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 UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
//Element所对应的class类
protected Class getBeanClass(Element element){
return User.class;
}
//从element中解析并获取对应的元素
protected void doParse(Element element, BeanDefinitionBuilder bd){
String name = element.getAttribute("name");
String email = element.getAttribute("email");
//将获取到的元素添加到BeanDefinitionBuilder中
if (StringUtils.hasText(name)){
bd.addPropertyValue("name",name);
}
if (StringUtils.hasText(email)){
bd.addPropertyValue("email",email);
}
}
}
(4) 创建一个继承于NamespaceHandlerSupport的Handler
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class UserNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
}
(5) 对于 Spring.handler和Spring.schemas的编写,注意的是这两个文件一般默认放在resource/META-INT文件下,可以通过Spring扩展或修改源码的方法来改路径
- Spring.handlers
http\://www.jianshu.com/schema/user=com.sgcc.bean.UserNamespaceHandler
- Spring.schemas
http\://www.jianshu.com/schema/user.xsd=user.xsd
- 编写配置文件
<?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:myTag="http://www.jianshu.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.jianshu.com/schema/user http://www.jianshu.com/schema/user.xsd ">
<myTag:user id="user" email="12233445566@qq.com" userName="chenssy"/>
</beans>
- 测试类
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
User user = (User) context.getBean("user");
System.out.println(user.getUserName() + "----" + user.getEmail());
- 最后的运行结果如下:
上面就是我们通过实现自定义标签简单的用法,我们在开发中常用的自定义的标签是<tx:annotation-driven>,接着我们来看解析的过程:
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
/**
*
* @param ele
* @param containingBd 父类的beanDefinition,如果是顶层父类beanDefinition将设置为null
* @return
*/
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
//获取对应的命名空间
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//根据命名空间构建一个NamespaceHandler实例
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
//解析过程
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
上述就是解析自定义标签的过程,这里简单的总结下解析的每一步:
- 首先是对命名空间的获取
BeanDefinitionParserDelegate.java
//获取命名空间的URL: http://www.springframework.org/schema/beans
@Nullable
public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}
- 通过我们拿到的命名空间来构建NamespaceHandler实例
XmlReaderContext.java
<1>.获取一个NamespaceHandlerResolver的解析器
/**
* Return the namespace resolver.
* @see XmlBeanDefinitionReader#setNamespaceHandlerResolver
*/
public final NamespaceHandlerResolver getNamespaceHandlerResolver() {
return this.namespaceHandlerResolver;
}
DefaultNamespaceHandlerResolver.java
<2>.通过调用resolve方法构建一个NamespaceHandler实例
/**
* Locate the {@link NamespaceHandler} for the supplied namespace URI
* from the configured mappings.
* @param namespaceUri the relevant namespace URI
* @return the located {@link NamespaceHandler}, or {@code null} if none found
*/
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
//获取所有的已经配置了的handler映射器
Map<String, Object> handlerMappings = getHandlerMappings();
//根据我们的命名空间找到对应的处理器的名字
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
//如果是NamespaceHandler类型的
else if (handlerOrClassName instanceof NamespaceHandler) {
//直接从缓冲中获取即可
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");
}
//实例化NamespaceHandler类
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
//保存到handlerMappings中
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}
上述构建NamespaceHandler的过程中,可以看到通过getHandlerMappings方法来读取Spring.handlers配置文件同时缓存到map集合中.
/** Stores the mappings from namespace URI to NamespaceHandler class name / instance. */
@Nullable
private volatile Map<String, Object> handlerMappings;
/**
* Load the specified NamespaceHandler mappings lazily.
*/
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
//如果为null,说明没有被缓存,那么可以缓存了
if (handlerMappings == null) {
//
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
//加载handlerMappingsLocation
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
handlerMappings = new ConcurrentHashMap<>(mappings.size());
//将mappings合并到handlerMappings
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
- 解析过程
BeanDefinitionParserDelegate.java
handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
ParserContext.java
<1>.首先是获取一个ParserContext实例
//BeanDefinition如果此时为顶级父类,默认为null
public ParserContext(XmlReaderContext readerContext, BeanDefinitionParserDelegate delegate,
@Nullable BeanDefinition containingBeanDefinition) {
this.readerContext = readerContext;
this.delegate = delegate;
this.containingBeanDefinition = containingBeanDefinition;
}
<2>.通过parse方法解析
/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
//通过element和parserContext来构建BeanDefinitionParser实例
BeanDefinitionParser parser = findParserForElement(element, parserContext);
//1.如果parser不为null,调用parse方法解析,并返回结果
//为null时直接返回null
return (parser != null ? parser.parse(element, parserContext) : null);
}
NamespaceHandlerSupport.java
<3>.通过调用findParserForElement方法获取BeanDefinitionParser解析器实例
//获取beanDefinitionParser解析器实例
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
<4>.如果构建的BeanDefinitionParser解析器不为null,才是真正的解析过程
/** Constant for the "id" attribute. */
public static final String ID_ATTRIBUTE = "id";
/** Constant for the "name" attribute. */
public static final String NAME_ATTRIBUTE = "name";
@Override
@Nullable
public final BeanDefinition parse(Element element, ParserContext parserContext) {
//获取一个beanDefinition实例
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
//解析id属性
String id = resolveId(element, definition, parserContext);
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 = null;
//
if (shouldParseNameAsAliases()) {
//获取name属性
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
//将AbstractBeanDefinition转化为BeanDefinitionHolder实例
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
//注册
registerBeanDefinition(holder, parserContext.getRegistry());
//通知对应的事件监听器
if (shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
在上述的解析过程中,主要做了对AbstractBeanDefinition的转化为beanDefinitionHolder和注册的过程,实际上spring将真正解析自定义标签的任务交给了AbstractSingleBeanDefinitionParser#parseInternal()方法,接下来我们看看该方法:
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
//构建BeanDefinitionBuilder实例
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
//获取父类元素
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
//获取自定义标签的class
//其次是会调用自定义解析器中的getBeanClass方法
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
//beanClass为null
else {
//表明子类没有重写getBeanClass方法,则去尝试着检查是否重写getBeanClassName方法
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
//封装属性
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
if (containingBd != null) {
//封装Scope属性
// Inner bean definition must receive same scope as containing bean.
builder.setScope(containingBd.getScope());
}
//如果是懒初始化
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
//设置LazyInit属性
builder.setLazyInit(true);
}
//调用子类的doParse进行解析
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
简单的看一下parseInternal的解析过程:
- 首先是构建一个BeanDefinitionBuilder构造器实例
/**
* Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
*/
public static BeanDefinitionBuilder genericBeanDefinition() {
return new BeanDefinitionBuilder(new GenericBeanDefinition());
}
- 如果当前元素有parent元素,首先获取它的父元素
protected String getParentName(Element element) {
return null;
}
可以发现默认是没有实现的
- 其次是获取自定义标签的class
protected Class<?> getBeanClass(Element element) {
return null;
可以看到的是,没有实现默认允许让子类去实现它
- 接着是对属性的封装
- 调用子类的doParse方法进行解析
/**
* Parse the supplied {@link Element} and populate the supplied
* {@link BeanDefinitionBuilder} as required.
* <p>The default implementation delegates to the {@code doParse}
* version without ParserContext argument.
* @param element the XML element being parsed
* @param parserContext the object encapsulating the current state of the parsing process
* @param builder used to define the {@code BeanDefinition}
* @see #doParse(Element, BeanDefinitionBuilder)
*/
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
doParse(element, builder);
}
/**
* Parse the supplied {@link Element} and populate the supplied
* {@link BeanDefinitionBuilder} as required.
* <p>The default implementation does nothing.
* @param element the XML element being parsed
* @param builder used to define the {@code BeanDefinition}
*/
protected void doParse(Element element, BeanDefinitionBuilder builder) {
}
实际上该方法是可以允许子类去实现解析的逻辑,到这里关于自定义标签的解析全部说完,简单的来个小结
- 首先是对自定义标签命名空间(namespaceUri)的获取
- 其次是加载默认文件的source/META-INF下的spring.handlers并放入缓存中,以<namespaceUri,类路径>方式存放.
- 接着通过获取到的namespaceUri通过反射的方式来获取NamespaceHandler实例
- 然后调用NamespaceHandler#init方法初始化,实际上是注册对应的解析器和要获取的实例如:
public class UserNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
}
上述代码是我们自己实现的过程,底层是将我们的user实体和自定义解析器进行了相关的注册操作.
- 接着是调用NamespaceHandler#parse方法进行自定义标签的解析操作
- 在解析前首先通过方法findParserForElement方法来进行解析器的获取操作
- 其次是调用namespaceHandlerSupport#parser进行解析,主要是针对于AbstractBeanDefinition的转化和注册做了处理,然后是将真正的解析操作给了parseInternal方法来完成
- 在parseInternal 方法中进行了一系列的属性设置操作如beanClass scope LazyInit等.
- 最后调用子类的doParse方法进行解析的操作,我们也看到了是空实现,允许子类去做自己的解析逻辑处理
关于自定义标签的解析的过程这里就简单的说完了,接下来我们来了解spring是如何对beanDefinition的注册的过程......
网友评论