spring源码分析之自定义标签的解析

目录

        1、自定义标签的

  1.1、PO类、xsd文件          

  1.2、BeanDefinitionParser 实现类、NamespaceHandlerSupport实现类

  1.3、spring.handlers、spring.schemas文件

  1.4、测试、测试结果

  测试结果​   大致猜想一下spring对自定义标签的解析

2、自定义标签原理解析

     2.1、parseCustomElement方法

    2.2、resolve方法    

    2.3、parse方法    

    2.4、BeanDefintionParser的parse()方法

    2.5、parseInternal()方法

    2.6、自定义标签解析总结


        自定义标签的概念我们听起来可能很陌生,但是一提起事务相关的配置  没错这个就是我们所说的自定义标签的使用。下面我们通过一个例子来简单实现一个自定义标签的例子来熟悉一下自定义标签的用法,后面我们在对自定义标签的解析做源码上的分析。    

   1、自定义标签的

     1.1、PO类、xsd文件          

//普通的pojo类
public class User {
    private String name;
    private Integer age;
}

//xsd描述文件
// schema 中的targetNamespace、xmlns:tns为自定义的命名空间


    
           
             
             
             
          
      

     1.2、BeanDefinitionParser 实现类、NamespaceHandlerSupport实现类

/**
 * 自定义的标签元素的解析处理类
 * 通过注册到对应的命名空间上 遇到具体的自定义标签会通过该类进行解析
 */
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    @Override
    protected Class getBeanClass(Element element) {
        return User.class;
    }

   //具体的标签解析功能的实现
    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String name = element.getAttribute("name");

        String ageStr = element.getAttribute("age");

        if(StringUtils.hasText(name)){
            builder.addPropertyValue("name",name);
        }
        if(StringUtils.hasText(ageStr)){
            builder.addPropertyValue("age",Integer.parseInt(ageStr));
        }
    }
}


/**
 * 自定义的命名空间处理器
 * 在spring.handles会将命名空间和该类进行绑定 这样使用具体的命名空间来进行标签的解析
 * 而命名空间使用的是注册的BeanDefinitionParser来进行具体的解析
 */
public class MyNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        //注册新的解析器 对应user标签 对于命名空间下的所有user标签使用UserBeanDefinitionParser进行解析
        registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
    }
}


    1.3、spring.handlers、spring.schemas文件

       spring容器启动的时候会先加载spring.handlers、spring.schemas文件。

//spring.handlers
//将命名空间和对应的命名处理器做绑定,让spring根据不同的命名空间获取到其对应的命名处理器 
//从而进行相应的解析处理
http\://www.example.org/schema/user=com.xiu.customer.MyNamespaceHandler


//spring.schemas
//将对应的命名空间的xsd文件和真实的xsd文件做绑定 用来做自定义标签的校验
//使自定义标签使用者按照规范去使用标签,而非随意的添加或者修改标签的属性
http\://www.example.org/schema/user.xsd=user.xsd

  • spring xml的配置文件


       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:myname="http://www.example.org/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.example.org/schema/user http://www.example.org/schema/user.xsd

       ">
     
     

   

 

   1.4、测试、测试结果

//自定义标签测试
@Test
public void testCustomerTag(){
   XmlBeanFactory  beanFactory = new XmlBeanFactory(new ClassPathResource("spring-mytag.xml"));
   com.xiu.customer.User user = beanFactory.getBean("myUser",com.xiu.customer.User.class);

   System.out.println("获取到自定义标签解析出来的用户,用户姓名:"+user.getName()+", 年龄: "+user.getAge());
 }

  测试结果spring源码分析之自定义标签的解析_第1张图片   大致猜想一下spring对自定义标签的解析

         spring容器启动的时候会先加载spring.sechmas和spring.handlers文件。这两个文件前者定义了xsd的规范信息,让我得以按照xsd的规范进行自定义标签的编写。后者定义了命名空间对应的解析处理器,通过对应的处理器获得到对应的BeanDefinitionParser 对标签元素进行解析,并转换为BeanDefinition对象。

   2、自定义标签原理解析

          上文我们分析了默认标签和自定义标签,现在分析自定义标签解析 delegate.parseCustomElement(ele);

     2.1、parseCustomElement方法

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	//根据元素标签获取对应的命名空间 例子中命名空间为http\://www.example.org/schema/user
	String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
		return null;
	}
	//根据命名空间获取对应的NamespaceHandler
	//这里会根据spring.handlers来获取到对应命名空间的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;
	}
	//调用NamespaceHandler的parse进行标签元素解析
	return handler.parse(ele, new ParserContext(this.readerContext,
          this, containingBd));
}

    客户自定义解析标签流程:

  • 获取命名空间
  • 根据命名空间获取对应的handler,调用resolve()方法
  • 根据解析器解析对应的标签 parse()方法

2.2、resolve方法    

public NamespaceHandler resolve(String namespaceUri) {
	//获取命名空间和NamespaceHandler缓存 缓存中NamespaceHandler 可能是对应的class字符串
	//也可能是对应的NamespaceHandler实例
	//getHandlerMappings()方法也是从缓存中获取 如果缓存中没有则加载属性文件获取
	Map handlerMappings = getHandlerMappings();
	//从缓存中获取NamspaceHandler 如果空返回空
	Object handlerOrClassName = handlerMappings.get(namespaceUri);
	if (handlerOrClassName == null) {
		return null;
	}
	//是NamespaceHandler实例返回实例
	else if (handlerOrClassName instanceof NamespaceHandler) {
		return (NamespaceHandler) handlerOrClassName;
	}
	else {
		//不是实例获取class 去实例化NamespaceHandler对象
		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的init()方法
		//我们自定义的MyNamespaceHandler 的init()方法注册一个UserBeanDefinitionParser 
		//来解析我们编写的user.xsd对应的标签,这样spring会根据映射关系找到 
                //UserBeanDefinitionParser去解析自定义标签
				NamespaceHandler namespaceHandler = (NamespaceHandler) 
              BeanUtils.instantiateClass(handlerClass);
			namespaceHandler.init();
			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);
		}
	}
}

2.3、parse方法    

public BeanDefinition parse(Element element, ParserContext parserContext) {
   //根据element元素 从parserContext中获取对应的BeanDefinitionParser  
   //对于我们自定义标签解析使用的是UserBeanDefinitionParser实例
   BeanDefinitionParser parser = this.findParserForElement(element, parserContext);
   //UserBeanDefinitionParser 调用其parse()进行元素标签的解析
   return parser != null ? parser.parse(element, parserContext) : null;
  }

//获取对应的BeanDefinitionParser根据element的localName获取对应的BeanDefinitionParser 
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    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;
}

2.4、BeanDefintionParser的parse()方法

      上面的步骤获取到了对应的BeanDefinitionParser,接下来调用的其对应的parser(),首先是调用AbstractBeanDefinition类的

parser方法。

//调用BeanDefintion的parser()方法
public final BeanDefinition parse(Element element, ParserContext parserContext) {
      //调用其parseInternal()方法 进行element到BeanDefintion对象的转换 
      //该方法也是对自定义标签的解析
      AbstractBeanDefinition definition = this.parseInternal(element, parserContext);
   if (definition != null && !parserContext.isNested()) {
       try {
          //获取自定义标签的id
          String id = this.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 (this.shouldParseNameAsAliases()) {
              String name = element.getAttribute("name");
              if (StringUtils.hasLength(name)) {
                aliases = StringUtils.trimArrayElements
                 (StringUtils.commaDelimitedListToStringArray(name));
              }
          }
         //创建BeanDefintionHolder对象
         BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
         //注册对应的Bendefintion是对象
         this.registerBeanDefinition(holder, parserContext.getRegistry());
         if (this.shouldFireEvents()) {
               BeanComponentDefinition componentDefinition =
                  new BeanComponentDefinition(holder);
              this.postProcessComponentDefinition(componentDefinition);
              parserContext.registerComponent(componentDefinition);
         }
        } catch (BeanDefinitionStoreException var8) {
            String msg = var8.getMessage();
            parserContext.getReaderContext().error(msg != null ?
                  msg : var8.toString(), element);
                return null;
        }
    }
   return definition;
}

 2.5、parseInternal()方法

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        //创建BeanDefinitionBuilder对象
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        //解析自定义的标签的parentName,beanClass。beanClassName等属性 设置进入
        // BeanDefinitionBuilder 对象中
        String parentName = this.getParentName(element);
        if (parentName != null) {
            builder.getRawBeanDefinition().setParentName(parentName);
        }

        Class beanClass = this.getBeanClass(element);
        if (beanClass != null) {
            builder.getRawBeanDefinition().setBeanClass(beanClass);
        } else {
            String beanClassName = this.getBeanClassName(element);
            if (beanClassName != null) {
                builder.getRawBeanDefinition().setBeanClassName(beanClassName);
            }
        }

        builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
        BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
        if (containingBd != null) {
            builder.setScope(containingBd.getScope());
        }

        if (parserContext.isDefaultLazyInit()) {
            builder.setLazyInit(true);
        }
        //调用其doParse() 到这里和我们对应的注册器UserBeanDefintionParser实现的
        //doParse()方法印证上了。
        this.doParse(element, parserContext, builder);
        return builder.getBeanDefinition();
    }

2.6、自定义标签解析总结

           自定义标签解析和我们的猜想一致。

 

你可能感兴趣的:(spring源码)