Spring IOC源码篇六 核心办法obtainFreshBeanFactory.parseCustomElement

1.写在前面

本篇非常重要,主要是分析标签解析原理

2.准备工作

bean类

/**
* bean类
*/
public class Test {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

启动类

/**
* 启动类
*/
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application-beans.xml");
Test test = (Test) context.getBean("test");
System.out.println("test = " + test);
System.out.println("test username = " + test.getUsername());
System.out.println("test password = " + test.getPassword());
}
}

application-beans.xml

<?xml version="1.0" encoding="UTF-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:contect="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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">
  <contect:property-placeholder location="classpath:database.properties"/>
    <bean id="test" class="com.sctelcp.bigdata.spring.Test">
    <property name="id" value="1"/>
    <property name="name" value="yuriy"/>
  </bean>
  <alias name="test" alias="yuriy-test"/>
</beans>

database.properties

jdbc.username=root
jdbc.password=root

改动点说明:
1.新增xml namespace:
xmlns:contect=“http://www.springframework.org/schema/context”
2.新增xml schemaLocation:
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
3.新增自定义标签配置:
<contect:property-placeholder location=“classpath:database.properties”/>
在这里插入图片描述

3.前置知识:XML 约束规范

在 Spring 框架中,XSD(XML Schema Definition)和 DTD(Document Type Definition)是两种用于约束 XML 配置文件结构的规范。它们定义了 XML 文档中允许的元素、属性、嵌套关系等,确保配置文件的合法性。Spring 早期主要使用 DTD,后期逐渐转向 XSD,两者在功能和使用上有显著区别。
1.DTD(Document Type Definition):
Spring 早期版本(如 2.0 及之前)的 XML 配置主要依赖 DTD 约束,例如:

<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE beans PUBLIC "-//Spring//DTD Bean 2.0//EN"
  "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
  <beans>
      <bean id="userService" class="com.example.UserService">
      <property name="userDao" ref="userDao"/>
    </bean>
    <bean id="userDao" class="com.example.UserDao"/>
  </beans>

2.XSD(XML Schema Definition)
Spring 2.0 及之后版本引入 XSD,逐步替代 DTD 成为主要约束方式,尤其在支持命名空间扩展后(如 context、aop 等命名空间)。通过 xmlns 定义命名空间(如 xmlns:context 绑定 context 前缀与 http://www.springframework.org/schema/context URI)。通过 xsi:schemaLocation 绑定命名空间 URI 与对应的 XSD 文件地址(命名空间 URI=XSD 路径)。可以看到配置的xmlns和schemaLocation是一个网络地址。但是实际工作中我们在没有网络的情况下也可以开发。下面代码中会详解原理
核心的XSD文件:
spring-beans.xsd:定义基础 、 等标签。
spring-context.xsd:定义 context:component-scan、context:annotation-config 等标签例如:

<?xml version="1.0" encoding="UTF-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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">
  <context:property-placeholder location="classpath:database.properties"/>
    <bean id="test" class="com.sctelcp.bigdata.spring.Test">
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>
  <alias name="test" alias="yuriy-test"/>
</beans>

4.BeanDefinitionParserDelegate.parseCustomElement方法

开始解析自定义标签context
在这里插入图片描述
Let’s go

/**
* 根据自定义命名空间标签的命名空间 URI,找到对应的 NamespaceHandler
* (命名空间处理器),并委托处理器解析标签内容,生成 BeanDefinition
* (或完成其他配置逻辑)
*/
public BeanDefinition parseCustomElement(Element ele) {
// 调用重载方法
return parseCustomElement(ele, null);
}
/**
* 方法重载
*/
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取自定义标签的命名空间URI
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据URI查找对应的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;
}
// 委托处理器解析标签,返回生成的BeanDefinition
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
这里详细说明一下readerContext参数和getNamespaceHandlerResolver()是哪里被赋值的
readerContext:

/**
* XmlBeanDefinitionReader.registerBeanDefinitions方法中
* createReaderContext(resource)创建了readerContext
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
/**
* XmlBeanDefinitionReader.createReaderContext方法
*/
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
/**
* XmlBeanDefinitionReader.getNamespaceHandlerResolver方法
* createDefaultNamespaceHandlerResolver()创建了DefaultNamespaceHandlerResolver
*/
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return this.namespaceHandlerResolver;
}

5.DefaultNamespaceHandlerResolver.resolve方法

/**
* 根据传入的命名空间 URI,从预加载的映射关系中找到对应的 NamespaceHandler
* 实例(或其类名),完成实例化、初始化后返回。
*/
public NamespaceHandler resolve(String namespaceUri) {
// 获取命名空间URI与处理器的映射关系(从spring.handlers加载)
Map<String, Object> handlerMappings = getHandlerMappings();
  // 根据URI从映射中获取处理器对象或类名
  Object handlerOrClassName = handlerMappings.get(namespaceUri);
  if (handlerOrClassName == null) {
  // 无对应处理器,返回null
  return null;
  }
  else if (handlerOrClassName instanceof NamespaceHandler) {
  // 无对应处理器,返回null
  return (NamespaceHandler) handlerOrClassName;
  }
  else {
  // 若为类名(首次加载),实例化并初始化处理器
  String className = (String) handlerOrClassName;
  try {
  // 加载处理器类
  Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
    // 校验是否实现了NamespaceHandler接口
    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);
    }
    }
    }

6.DefaultNamespaceHandlerResolver.getHandlerMappings方法

/**
* 懒加载并缓存 META-INF/spring.handlers 文件中的配置,
* 返回 “命名空间 URI→处理器类名” 的映射关系。
*/
private Map<String, Object> getHandlerMappings() {
  // 先从缓存获取映射关系
  Map<String, Object> handlerMappings = this.handlerMappings;
    // 这里说个问题,当大家debug到这里的时候handlerMappings已经不为null了,这是因为idea debug
    // 模式调用了该类的toString方法,toString方法调用了当前方法已经加载过了
    if (handlerMappings == null) {
    // 双重检查锁:确保多线程环境下仅加载一次
    synchronized (this) {
    handlerMappings = this.handlerMappings;
    if (handlerMappings == null) {
    if (logger.isTraceEnabled()) {
    logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
    }
    try {
    // 加载类路径下所有META-INF/spring.handlers文件
    // this.handlerMappingsLocation在DefaultNamespaceHandlerResolver构造器中被赋值的值就是当前类的
    // 常量DEFAULT_HANDLER_MAPPINGS_LOCATION,加载地址:META-INF/spring.handlers
    Properties mappings =
    PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
    if (logger.isTraceEnabled()) {
    logger.trace("Loaded NamespaceHandler mappings: " + mappings);
    }
    // 将Properties转换为ConcurrentHashMap(线程安全)
    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;
      }

7.PropertiesLoaderUtils.loadAllProperties方法

/**
* 从类路径下查找所有名称为 resourceName 的资源文件(可能分布在不同 JAR 包中),
* 读取并合并其内容为一个 Properties 对象。
*/
public static Properties loadAllProperties(String resourceName, @Nullable ClassLoader classLoader) throws IOException {
Assert.notNull(resourceName, "Resource name must not be null");
// 确定类加载器(优先使用传入的classLoader,否则使用默认类加载器)
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = ClassUtils.getDefaultClassLoader();
}
// 查找类路径下所有名为resourceName的资源(可能来自多个JAR包)
Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) :
  ClassLoader.getSystemResources(resourceName));
  // 初始化Properties对象,用于合并所有资源内容
  Properties props = new Properties();
  // 遍历所有找到的资源,读取并合并内容
  while (urls.hasMoreElements()) {
  // 获取资源的URL(如jar:file:/xxx/spring-context.jar!/META-INF/spring.handlers)
  URL url = urls.nextElement();
  // 打开资源连接
  URLConnection con = url.openConnection();
  // 根据是否允许缓存设置连接属性
  ResourceUtils.useCachesIfNecessary(con);
  // 读取资源内容(支持Properties和XML格式)
  try (InputStream is = con.getInputStream()) {
  // XML格式(.xml)
  if (resourceName.endsWith(XML_FILE_EXTENSION)) {
  if (shouldIgnoreXml) {
  throw new UnsupportedOperationException("XML support disabled");
  }
  // 从XML加载配置
  props.loadFromXML(is);
  }
  else {
  // 普通Properties格式(.properties)从流加载配置
  props.load(is);
  }
  }
  }
  return props;
  }

这里我给出spring-context jar包中的样列
在这里插入图片描述

8.ContextNamespaceHandler.init方法

这里我们回到第五步namespaceHandler.init();这行。记住我们当前debug是在解析context的自定义标签
<context:property-placeholder location=“classpath:database.properties”/>
第五步中handlerMappings已经加载了所有的命名空间URI和处理类的映射关系了。在第五步的Object handlerOrClassName = handlerMappings.get(namespaceUri);中获取context标签命名空间对应的处理类ContextNamespaceHandler。然后调用namespaceHandler.init();完成handler的初始化。这里我们看下初始化具体做了哪些事儿。其他的标签解析步骤同样如此

/**
* 注册 context 命名空间下所有标签的解析器,建立 “标签名称→解析器” 的映射关系。
* 当 Spring 解析 XML 中 <context:component-scan>、<context:annotation-config> 等标签时,
  * 会根据该映射找到对应的解析器,执行具体的解析逻辑(如扫描包、注册 Bean 定义等)。
  * 这里我们可以看到我们比较熟悉的两个标签property-placeholder和component-scan
  */
  public void init() {
  // 调用父类方法,注册<context:property-placeholder>标签的解析器
    registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
    // 调用父类方法,注册<context:property-override>标签的解析器
      registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
      // 调用父类方法,注册<context:annotation-config>标签的解析器
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        // 调用父类方法,注册<context:component-scan>标签的解析器
          registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
          // 调用父类方法,注册<context:load-time-weaver>标签的解析器
            registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
            // 调用父类方法,注册<context:spring-configured>标签的解析器
              registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
              // 调用父类方法,注册<context:mbean-export>标签的解析器
                registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
                // 调用父类方法,注册<context:mbean-server>标签的解析器
                  registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
                  }
                  /**
                  * 可以看到就是将映射关系存放到父类的parsers中
                  * private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
                    */
                    protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
                    this.parsers.put(elementName, parser);
                    }

9.ContextNamespaceHandler.parse方法

回到第四步的
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
这里的handler就是ContextNamespaceHandler

PropertyPlaceholderBeanDefinitionParser类图
在这里插入图片描述

/**
* 这里再次贴处理我们当前的标签
* <context:property-placeholder location="classpath:database.properties"/>
*/
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 这里就是获取对应标签(property-placeholder)的解析器,
// 这里我们获取到的是第八步中注册的PropertyPlaceholderBeanDefinitionParser请参考上图的类图关系
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
/**
* 这里来到了AbstractBeanDefinitionParser.parse
*/
public final BeanDefinition parse(Element element, ParserContext parserContext) {
// 调用子类实现的parseInternal,解析标签生成BeanDefinition(核心逻辑由子类实现)第十步详解有
AbstractBeanDefinition definition = parseInternal(element, parserContext);
// 非嵌套标签(顶级标签)且解析成功时,执行注册流程
if (definition != null && !parserContext.isNested()) {
try {
// 解析或生成Bean的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);
}
// 处理别名(若需要从name属性解析)
String[] aliases = null;
if (shouldParseNameAsAliases()) {
// 从name属性获取别名(逗号分隔)
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
// 封装为BeanDefinitionHolder(包含定义、ID、别名)
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
// 注册BeanDefinition到容器,解析标签最终都是将其生成bean定义信息并注册到bean定义容器中
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;
}

10.AbstractSingleBeanDefinitionParser.parseInternal方法

/**
* 构建 BeanDefinitionBuilder 实例,设置 Bean 的基础属性(父类、类名、作用域、
* 延迟初始化等),并调用子类的 doParse 方法解析标签的自定义属性 / 子元素,
* 最终生成 AbstractBeanDefinition。
*/
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
// 创建BeanDefinitionBuilder(用于构建GenericBeanDefinition)
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
// 设置父Bean名称(子类可通过getParentName重写,默认null)
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
// 设置Bean的类信息(优先类对象,其次类名)
Class<?> beanClass = getBeanClass(element);
  if (beanClass != null) {
  builder.getRawBeanDefinition().setBeanClass(beanClass);
  }
  else {
  String beanClassName = getBeanClassName(element);
  if (beanClassName != null) {
  builder.getRawBeanDefinition().setBeanClassName(beanClassName);
  }
  }
  // 设置BeanDefinition的资源来源(用于错误定位)
  builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
  // 设置作用域(嵌套Bean继承父Bean的作用域,顶级Bean默认singleton)
  BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
  if (containingBd != null) {
  // Inner bean definition must receive same scope as containing bean.
  builder.setScope(containingBd.getScope());
  }
  // 设置延迟初始化(继承全局默认配置,默认false)
  if (parserContext.isDefaultLazyInit()) {
  // Default-lazy-init applies to custom bean definitions as well.
  builder.setLazyInit(true);
  }
  // 委托子类解析自定义属性/子元素(核心扩展点)
  doParse(element, parserContext, builder);
  // 生成并返回AbstractBeanDefinition(GenericBeanDefinition)
  return builder.getBeanDefinition();
  }

11.写在最后

本篇主要讲解了自定义标签的解析原理,是怎么获取到标签解析类的,怎么完成标签解析,最终将生成的BeanDefinition注册到容器中的。

posted on 2025-10-13 16:25  slgkaifa  阅读(5)  评论(0)    收藏  举报

导航