1 parseCustomElement
当完成从配置文件到 Document 的转换并提取对应的 root 后,就开始了所有元素的解析,而在这过程中便开始了默认标签与自定义标签两种梢式的区分,函数如下:
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)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
上面代码可以看出,当 Spring 拿到一个元素时首先要做的是根据命名空间进行解析,如果是默认的命名空间,则使用parseDefaultElement 方法进行元素解析,否则使用parseCustomElement 方法进行解析。
2 自定义标签的使用
Spring 提供了可扩展的 Schema 的支持(需要 spring-core 包),解析工作主要包含以下几个步骤。
- 创建一个需要扩展的组件。
- 创建一个 XSD 文件描述组件的内容。
- 创建一个文件,实现 BeanDefinitionParser 接口,用来解析 XSD 文件中的定义和组件定义。
- 创建一个 Handler 文件,扩展自 NamespaceHandlerSupport,目的是将组件注册到 Spring 容器中。
- 编写 spring.handlers 和 spring.schemas 文件。
应用实例: 1)创建一个 POJO
public class User {
private String userName;
private String email;
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;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", email='" + email + '\'' +
'}';
}
}
2)定义一个 XSD 文件描述组件内容 在 META-INF 下定义
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.lyb.xyz/schema/user"
xmlns:tns="http://www.lyb.xyz/schema/user">
<element name="user">
<complexType>
<attribute name="id" type="string"/>
<attribute name="userName" type="string"/>
<attribute name="email" type="string"/>
</complexType>
</element>
</schema>
上述 XSD 文件描述了一个新的 targetNamespace,并定义了一个 name 为 user 的 element。
3)创建一个类实现 BeanDefinitionParser 接口,用来解析 XSD 文件中的定义和组件定义。
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String userName = element.getAttribute("userName");
String email = element.getAttribute("email");
builder.addPropertyValue("userName", userName);
builder.addPropertyValue("email", email);
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
super.doParse(element, parserContext, builder);
}
}
4)创建一个 Hadnler 文件,扩展自 NamespaceHandlerSupport,目的是将组件注册到 Spring 容器。
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
以上代码当遇到自定义标签 <user:aa 这样类似于以 user 开头的元素,就会把这个元素委托给对应的 UserBeanDefinitionParser 解析。
5)编写 spring.handlers 和 spring.schemas 文件。默认位置在 /META-INF/ 文件夹下。
spring.schemas
http\://www.lyb.xyz/schema/user.xsd=META-INF/spring-user.xsd
spring.handlers
http\://www.lyb.xyz/schema/user=org.springframework.customtag.MyNamespaceHandler
6)测试配置文件
<?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.lyb.xyz/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.lyb.xyz/schema/user http://www.lyb.xyz/schema/user.xsd">
<mytag:user id="userBean" userName="里斯" email="14813133"/>
</beans>
7)测试类
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("test/customtag/customTag.xml");
User userBean = (User) context.getBean("userBean");
System.out.println(userBean);
}
}
3 自定义标签的解析
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
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));
}
上述代码的解析步骤:
- 获取对应的命名空间
- 根据命名空间从上下文中获取 NamespaceHandler
- 调用自定义的 NamespaceHandler 进行解析
3.1 获取标签的命名空间
标签的解析都是从命名空间的提取开始,无论是区分默认标签和自定义标签还是区分自定义标签中不同的处理器都是以标签所提供的名称空间为基础的。
public String getNamespaceURI(Node node) {
return node.getNamespaceURI();
}
3.2 提取自定义标签处理器
有了命名空间,就可以进行 NamespaceHandler 提取了。由this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri) 该方法实现,在 readerContext 初始化的时候 namespaceHandlerResolver 已经被初始化为了 DefaultNamespaceHandlerResolver 了。
namespaceHandlerResolver 初始化:
XmlBeanDefinitionReader.java
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
return new DefaultNamespaceHandlerResolver(cl);
}
进入 DefaultNamespaceHandlerResolver 的 resolve 方法。
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
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) 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);
}
}
}
上述代码主要步骤: 1)获取所有已经配置的 handler 映射,懒加载, getHandlerMappings 该操作将 spring.handlers 文件中的所有映射读取放入缓存 handlerMappings 中。
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
handlerMappings = new ConcurrentHashMap<>(mappings.size());
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;
}
2)根据命名空间在 handlerMappings 中查找对应的信息,如果已经解析过则直接返回。 3)如果没有解析过则反射生成 NamespaceHandler 实例。 4)调用 namespaceHandler 的初始化方法,注册 parser,将 namespaceHandler 放入缓存。
3.3 标签解析
得到了解析器以及要分析的元素后, Spring 就可以将解析工作委托给自定义解析器去解析了。在 Spring 中的代码为:
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
parse 方法在父类 NamespaceHandlerSupport 中实现。
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
解析过程: 1)找到对应的 parser
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;
}
2)调用 parser.parse(element, parserContext) 方法
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
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()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
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;
}
3)解析获得 AbstractBeanDefinition 说是对自定义配置文件的解析。但是,在这个函数中大部分的代码是用来处理将解析后的 AbstractBeanDefinition 转化为 BeanDefinitionHolder 并注册的功能,而真正去做解析的事情委托给了 parseInternal 方法。正是这句代码调用了自定义的解析函数。
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
String beanClassName = 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(element, parserContext, builder);
return builder.getBeanDefinition();
}
虽然在实例中我们定义 UserBeanDefinitionParser,但是在其中我们只是做了与自己业务逻辑相关的部分。在这个处理过程中同样也是按照 Spring 标准的处理方式进行,包括创建 BeanDefinition 以及进行相应默认属性的设置,并暴露出一些接口来供用户实现个性化的业务。
|