Spring源码分享【一】BeanDefinition 的获取

一、前言

Spring 中非常核心的一个点就是工厂,他不仅仅是将对象创建出来,还需要将对象存储起来(类似于容器,能够进行存储)。在学习基础的时候,对于一个对象,实际上是有两种,一种类型的对象是单实例对象,另一种就是多实例对象,而在Spring之中只存放单实例对象。

那么在Spring之中,如何去存储这个单实例对象集合呢?

这里我们可以想一下,哪一种容器能够存放多个对象,并且取起来比较方便呢,答案肯定是Map。

对于如何创建对象?如何进行对象的存储,通过源码的阅读,都将会明白!☀️

二、Spring 中的工厂

首先,来看看Spring中都有哪些工厂?,并且从这里开启Spring的源码阅读之路

刚开始学习Spring的时候,我们使用的是ApplicationContext这个工厂,并且通过这个工厂完成对象获取工作,但是ApplicationContext实际上是一个高级工厂。使用ApplicationContext工厂获取对象的代码如下:

 ApplicationContext ac = new ClassPathXmlApplicationContext("/applicationContext.xml");
 ​



 ac.getBean("user");

之所以说它高级,是因为他为我们集成了一些底层工厂的功能,并且简化了我们创建工厂的步骤,通过这种方式获取对象非常的简单,这也是Spring不断完善的过程。在Spring中,最为底层的工厂叫做:BeanFactory。在这个类的源码之上,有一行注释:

 The root interface for accessing a Spring bean container.

他是作为 Spring 的根工厂的,我们首先来看一下,这个BeanFactory 的继承体系

image-20230713214336345.png

既然这些工厂都具有各自的能力,那么那些工厂是集这些工厂的能力于一身?实际上是有两个:

  • ApplicationContext
  • DefaultListableBeanFactory

对于DefaultListableBeanFactory,他还有一个子类XmlBeanFactory,能够使用基于Xml配置的信息,完成对象的创建,而他能够完成这样的工作,主要依赖于XMlBeanDefinitionReader完成工作。

这里我们通过XmlBeanFactory这个工厂来改造一下上面通过高级工厂获取对象的代码,代码如下:

 // 1. 读取配置文件,将XML配置文件封装为对象
 BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("/applicationContext.xml"));
 ​

 // 2. 获取对象
 User user = (User) beanFactory.getBean("user");

在使用Spring工厂之后,可以思考一下下面几个问题:

1)怎么读取配置文件

2)读取完配置文件之后,配置文件之中的信息在Spring之中又是如何进行体现的

3)如何根据配置信息,创建对象

4)所创建对象的生命周期

这四个问题其实也是IOC之中的核心,在后面的源码解析过程之中,也会慢慢进行解决!

回归正题,继续来看我们所编写的代码,相比于ApplicationContext,这里传入配置的文件的方式有所不同,XMLBeanFactory是通过传入一个ClassPathResource,而高级工厂是直接传入配置文件的路径名即可。那么,ClassPathResource 是什么?起到了什么作用?

在 Java 之中,将不同源的资源抽象为 URL,通过注册不同的 handler 来处理不同来源的资源的读取逻辑,一般 handler 的类型使用不同的前缀来识别,比如说:”file:”、”http:”等,然后URL没有默认定义相对 classpath 或者 servletContext 等资源的 handler,虽然可以自己注册 URLStreamHandler 来解析特定的URL前缀,然而这需要料及URL的实现机制,所以 Spring 对其内部使用的资源实现了自己的抽象接口,Resource 接口来封装底层资源。这里,我们首先查看继承关系:

ClassPathResource.png

对于不同来源·的资源文件都有对应的 Resource 实现,有了这个接口,便可以对所有资源文件进行统一处理。

image-20230713225542694.png

我们最终是将一个 Resource 对象传入了 XMLBeanFactory。我们可以看看XMLBeanFactory中如何使用的?这里给出对应的源码

 public class XmlBeanFactory extends DefaultListableBeanFactory {
     private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
 ​

     public XmlBeanFactory(Resource resource) throws BeansException {
         this(resource, (BeanFactory)null);
     }

 ​
     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
         super(parentBeanFactory);
         this.reader.loadBeanDefinitions(resource);
     }
 }

补充内容:

Idea 在写这个类的时候,说明这个类已经过时了,目前推荐的使用方式为:DefaultListableBeanFactory + XmlBeanDefinitionReader 的形式

 DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

 Resource resource = new ClassPathResource("/application.xml");
 XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);

 xmlBeanDefinitionReader.loadBeanDefinitions(resource);
 User user = (User) factory.getBean("user");

我们接下来继续进行分析内容

在构造方法之中,又会调用本类的另外一个构造方法,而这个构造方法的第一行,又会去调用父类的构造方法。作为Spring来讲,是允许一个工程之中,有多个Spring工厂同时出现,最为典型的场景就是 Spring MVC 中的父子容器。而下面这一行,才是解析资源的真正实现:

 this.reader.loadBeanDefinitions(resource);

也能够看出XmlBeanDefinitionReader这个类才是解析XML,封装对象的核心。而这个对象在构造的时候,传入的是一个工厂!这个时候我们来写一段代码测试一下:

 DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

 ​



 XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);

 ​
 Resource resource = new ClassPathResource("/application.xml");
 ​
 xmlBeanDefinitionReader.loadBeanDefinitions(resource);
 ​

 User user = (User) factory.getBean("user");

通过这段代码,也同样能够获取到对象!

接下来我们分析一下loadBeanDefinitions这个方法中的核心过程:

image-20230713235230141转存失败,建议直接上传图片文件

Document 对象 和Spring 没有关系,他是XML解析所封装的对象,然后再把 Document 对象转为 我们所需要的类型

而这个registerBeanDefinitions方法,才是解析BeanDefinition对象的核心,而这个方法也是从属于:XMLPathDefinitionReader

 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
     BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
     // 1. 目前这个注册器注册了多少个
     int countBefore = getRegistry().getBeanDefinitionCount();
     // 2. 解析核心
     documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
     return getRegistry().getBeanDefinitionCount() - countBefore;
 }

最为核心的代码是registerBeanDefinitions,最下面就是这个方法

 // 这个 root 就是根标签 beans
 protected void doRegisterBeanDefinitions(Element root) {
     BeanDefinitionParserDelegate parent = this.delegate;
     this.delegate = createDelegate(getReaderContext(), root, parent);
     if (this.delegate.isDefaultNamespace(root)) {
         // 1. PROFILE_ATTRIBUTE : profile,这个标签完成不同环境的切换
         String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
         if (StringUtils.hasText(profileSpec)) {
             String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                         profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                 // We cannot use Profiles.of(...) since profile expressions are not supported
                 // in XML config. See SPR-12458 for details.
                 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                     if (logger.isDebugEnabled()) {
                         logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                 "] not matching: " + getReaderContext().getResource());
                     }
                     return;
                 }
         }

     }


     // 空实现
     preProcessXml(root);
     // 2. 传递根标签 和 注册器
     parseBeanDefinitions(root, this.delegate);
     // 空实现
     postProcessXml(root);
     this.delegate = parent;
 }

1)首先开始对 PROFILE_ATTRIBUTE属性进行解析,而这个属性是为了完成不同环境的切换

1)配置文件之中

 <?xml version="1.0" encoding="UTF-8"?>



 <beans profile="dev" xmlns="http://www.springframework.org/schema/beans"
   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">
 ​

 <bean id="user" class="com.haolong.spring.entity.User">
    <property name="username">
        <value>haoolong</value>
    </property>
    <property name="password">
        <value>123</value>
    </property>
 </bean>
 </beans>

2)在 web.xml 之中做配置

 <?xml version="1.0" encoding="UTF-8"?>



 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
     version="4.0">
 <context-param>
    <param-name>Spring.profiles.active</param-name>
    <param-value>dev</param-value>
 </context-param>
 </web-app>

2)对于prePorcessXMlprocessProcessXml来说,都是空实现,这两个方法是为了子类而设计的,这两个方法都是属于DefaultBeanDefinitionDocumentReader,如果说子类需要在Bean解析前或者后做一些事的时候,只需要重写这两个方法就行了,这也是模板方法模式的体现。

3)处理完成了profile属性之后,就开始了进行了BeanDefinition对象的解析工作,与之对应的就是上述代码中的:parseBeanDefinitions(root, this.delegate);方法,在这个方法之中,核心有两个分支,一类就是解析基本标签,另一种就是解析自定义标签

基本标签:XXX

自定义标签:新的命名空间标签,如<context:xxxx />

 // 所属的类:DefaultBeanDefinitionDocumentReader
 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)){
                     // 解析基本标签,就是bean标签
                     parseDefaultElement(ele, delegate);
                 } else {            
                     // 解析自定义标签,比如说 context:commponent-scan
                     delegate.parseCustomElement(ele);
                 }
             }
         }
     }
     else {
         delegate.parseCustomElement(root);
     }


 }

这里我们首先来看一下解析默认标签

三、解析默认标签

在开始具体分析标签的解析之前,首先透露一下,在Spring 之中,会把配置文件之中的每一个 bean 解析为 BeanDefinition 对象

 // 所属于的类:DefaultBeanDefinitionDocumentReader
 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
     // 1. 对 import 标签做处理,用来引入其他的配置文件
     if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
         importBeanDefinitionResource(ele);
     }

     // 2. 对 alias 标签做处理,别名
     else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
         processAliasRegistration(ele);
     }
     // 3. 对 bean 标签做处理
     else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
         processBeanDefinition(ele, delegate);
     }
     // 4. 对 beans 标签做处理
     else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
         // recurse
         doRegisterBeanDefinitions(ele);
     }
 }

在标签的解析中,对于bean标签是最为复杂且重要的,所以我们首先来看一下这个方法的实现

 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
     // 1. 解析,BeanDefinationHolder 实际上是 BeanDefination 的封装
     BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
     if (bdHolder != null) {
         // 2. 如果说Bean标签中,有自定义标签
         bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
         try {
             // 3. BeanDefinition 的注册
             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
         }
         catch (BeanDefinitionStoreException ex) {
             getReaderContext().error("Failed to register bean definition with name '" +
                                      bdHolder.getBeanName() + "'", ele, ex);
         }
         // 4. 发送 BeanDefination 注册完成的事件,这里其实是一个可扩展的点,这个方法最后调用的是一个空方法
         getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
     }

 }

首先给出了各个方法的大体含义,接下来,我们逐步进行分析:

1)第一步:获取 BeanDefinitionHolder

会把Bean标签所涉及的标签都解析出来

 @Nullable
     public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
         // 1. 获取 ID 属性
         String id = ele.getAttribute(ID_ATTRIBUTE);
         // 2. 获取 name 属性
         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
         // 3. 如果说有 name 属性,就把这个 name 拆分成数组
         List<String> aliases = new ArrayList<>();
         if (StringUtils.hasLength(nameAttr)) {
             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
             aliases.addAll(Arrays.asList(nameArr));
         }
         // 4. 赋值
         String beanName = id;
         // 5. 如果说这个标签没有没有设置 id 属性,但是 设置了 name 属性,就将 name 属性之中的第一个值设置为 id 属性
         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
             beanName = aliases.remove(0);
             if (logger.isTraceEnabled()) {
                 logger.trace("No XML 'id' specified - using '" + beanName +
                         "' as bean name and " + aliases + " as aliases");
             }
         }
         // 6. 确保这个 beanName 是唯一的
         if (containingBean == null) {
             checkNameUniqueness(beanName, aliases, ele);
         }
 ​
         // 7. 解析bean 标签的其他属性,创建 GenericBeanDefinition 对象
         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
         if (beanDefinition != null) {
             if (!StringUtils.hasText(beanName)) {
                 try {
                     if (containingBean != null) {
                         beanName = BeanDefinitionReaderUtils.generateBeanName(
                                 beanDefinition, this.readerContext.getRegistry(), true);
                     }
                     else {
                         beanName = this.readerContext.generateBeanName(beanDefinition);
                         // Register an alias for the plain bean class name, if still possible,
                         // if the generator returned the class name plus a suffix.
                         // This is expected for Spring 1.2/2.0 backwards compatibility.
                         String beanClassName = beanDefinition.getBeanClassName();
                         if (beanClassName != null &&
                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                             aliases.add(beanClassName);
                         }
                     }
                     if (logger.isTraceEnabled()) {
                         logger.trace("Neither XML 'id' nor 'name' specified - " +
                                 "using generated bean name [" + beanName + "]");
                     }
                 }
                 catch (Exception ex) {
                     error(ex.getMessage(), ele);
                     return null;
                 }
             }
             String[] aliasesArray = StringUtils.toStringArray(aliases);
             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
         }
 ​
         return null;
     }

在分析这个过程之中,我们也获取了一个比较重要的信息:对于BeanDefinitionHolder 之中所包裹的 BeanDefintion 具体类型是 GenericBeanDefinition

BeanDefinition 相关的概念

这里首先可以来看看,这个对象的继承关系体系

image-20230714221356628.png

在我们使用的过程之中使用的是:GenericBeanDefinition,对于里面其他的,等我们实际用的时候,在进行补充

到这里为止,我们最终就获得了BeanDefinition,这就完成了第一步,解析XML 为BeanDefinition对象,

2)第二步:如果说Bean标签中,有自定义标签

对于这一步,几乎不会使用的到

3)第三步:注册BeanDefinition

在我们这篇文章开始的时候,我们就猜想Spring之中的对象,实际上是存放在 Map 之中的,接下来就来验证一下,我们的猜想!

 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

我们来看一下这个方法之中,到底做了些什么?

对于这个方法的第一个参数:传入的 BeanDefinitionHolder,这就是我们需要注册的信息,而对于其中的第二个参数,实际上是:

 AbstractBeanDefinitionReader 中的 BeanDefinitionRegistry
 /**
 * Register the given bean definition with the given bean factory.
 * 中文:向给定的 bean 工厂注册给定的 bean 定义。
 * @param definitionHolder the bean definition including name and aliases
 * @param registry the bean factory to register with
 * @throws BeanDefinitionStoreException if registration failed
 */
 public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
     throws BeanDefinitionStoreException {
 ​
     // Register bean definition under primary name.
     String beanName = definitionHolder.getBeanName();
     registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
 ​
     // Register aliases for bean name, if any.
     String[] aliases = definitionHolder.getAliases();
     if (aliases != null) {
         for (String alias : aliases) {
             registry.registerAlias(beanName, alias);
         }

     }


 }

而这个里面最为核心的方法就是:registerBeanDefinition,通过Idea 不断往下面追,发现这个方法有3个子类之中都实现了

image-20230722153430449.png

那我我们应该看哪一个呢?答案是:DefaultListableBeanFactory,这个才是 Spring 之中集大成的工厂

 // DefaultListableBeanFactory
 @Override
 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
     throws BeanDefinitionStoreException {
 ​

     Assert.hasText(beanName, "Bean name must not be empty");
     Assert.notNull(beanDefinition, "BeanDefinition must not be null");
 ​

     if (beanDefinition instanceof AbstractBeanDefinition) {
         try {
             ((AbstractBeanDefinition) beanDefinition).validate();
         }
         catch (BeanDefinitionValidationException ex) {
             throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                    "Validation of bean definition failed", ex);
         }
     }

     // 1. 根据 beanName 来看一下,是否已经注册过了
     /**
     * private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
     */
     BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
     if (existingDefinition != null) {
         if (!isAllowBeanDefinitionOverriding()) {
             throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
         }
         else if (existingDefinition.getRole() < beanDefinition.getRole()) {
             // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
             if (logger.isInfoEnabled()) {
                 logger.info("Overriding user-defined bean definition for bean '" + beanName +
                             "' with a framework-generated bean definition: replacing [" +
                             existingDefinition + "] with [" + beanDefinition + "]");
             }
         }
         else if (!beanDefinition.equals(existingDefinition)) {
             if (logger.isDebugEnabled()) {
                 logger.debug("Overriding bean definition for bean '" + beanName +
                              "' with a different definition: replacing [" + existingDefinition +
                              "] with [" + beanDefinition + "]");
             }
         }
         else {
             if (logger.isTraceEnabled()) {
                 logger.trace("Overriding bean definition for bean '" + beanName +
                              "' with an equivalent definition: replacing [" + existingDefinition +
                              "] with [" + beanDefinition + "]");
             }
         }
         this.beanDefinitionMap.put(beanName, beanDefinition);
     }
     else {
         if (hasBeanCreationStarted()) {
             // Cannot modify startup-time collection elements anymore (for stable iteration)
             synchronized (this.beanDefinitionMap) {
                 this.beanDefinitionMap.put(beanName, beanDefinition);
                 List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                 updatedDefinitions.addAll(this.beanDefinitionNames);
                 updatedDefinitions.add(beanName);
                 this.beanDefinitionNames = updatedDefinitions;
                 removeManualSingletonName(beanName);
             }
         }
         else {
             // Still in startup registration phase
             this.beanDefinitionMap.put(beanName, beanDefinition);
             this.beanDefinitionNames.add(beanName);
             removeManualSingletonName(beanName);
         }
         this.frozenBeanDefinitionNames = null;
     }
 ​
     if (existingDefinition != null || containsSingleton(beanName)) {
         resetBeanDefinition(beanName);
     }
     else if (isConfigurationFrozen()) {
         clearByTypeCache();
     }
 }
 ​

life_2.png
到这里为止:我们已经分析完成了基本标签的解析,BeanDefinition 对象,存储注册好的对象存储

接下来,我们画一个流程图:

四、解析自定义标签

那么,Spring 中如何解析自定义标签的?其中最为常见的自定义标签就是SpringMVC中的标签

 <?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:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/beans

             http://www.springframework.org/schema/beans/spring-beans.xsd

             http://www.springframework.org/schema/mvc
             https://www.springframework.org/schema/mvc/spring-mvc.xsd">
     <mvc:annotation-driven/>
 </beans>

我们首先看看SpringMVC中是如何实现的,当我们实现引入了,mvc:annotation-driven之后,SpringMVC的核心功能就被我们所引入。那么这个标签到底做了什么事?

1)这个mvc实际上是一个命名空间,mvc是这个空间的小名,而后面这一长串是他的大名,对应于下面这一部分。

2)xsd文件,实际上就是规定这个标签中能够定义内容。

自定义标签解读.png

那么这个文件在哪里,就需要有一个映射关系,存放这个映射关系的文件就是spring.schemas

 https://www.springframework.org/schema/mvc/spring-mvc.xsd=org/springframework/web/servlet/config/spring-mvc.xsd

这个命名空间中可以有很多的标签,如何解析?我们首先找到了spring.handlers,这个文件

 http://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler

进去发现MvcNamespaceHandler,每个子标签的解析又会交给对应的Parser来完成

自定义标签解读2.png

而在这类里面,对应于一个parse方法,返回值为BeanDefinition

所以说对于自定义标签的解析,是通过 Handler 找对应的 parser,最终由Parser完成解析,转为BeanDefinition

问题:如何自己实现一个自定义标签,标签如下:

 <haolong:user ....../>
 ​



 // 下面所建立的文件都是在 resources 下面建立一个META-INF 文件夹,在该文件件下面进行建立
  • 首先,想一下,这个标签中有什么内容? 建立user.xsd文件
 <?xml version="1.0" encoding="utf-8" ?>
 <schema xmlns="http://www.w3.org/2001/XMLSchema"
         targetNamespace="http://www.haolong.com/schema/user"
         xmlns:tns="http://www.haolong.com/schema/user"
          elementFormDefault="qualified">
     <element name="user">
         <complexType>
             <attribute name="id" type="string"></attribute>
             <attribute name="username" type="string"></attribute>
             <attribute name="password" type="string"></attribute>
         </complexType>
     </element>
 </schema>
  • 我在xml中写了一长串如何对应于上述具体文件?建立spring.schema 文件
 http://www.haolong.com/schema/user.xsd=META-INF/user.xsd
  • 标签如何被解析?被谁解析?首先是通过Handler 来找Parser解析,所以先需要一个Parser,继而需要一个Handler
 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 password = element.getAttribute("password");
         builder.addPropertyValue("username",username);
         builder.addPropertyValue("password",password);
     }
 }
 public class MyNamespaceHandler extends NamespaceHandlerSupport {
     @Override

     public void init() {
         registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
     }

 }

这次,我们的自定义标签已经完成了,这个时候就来试一试

 <?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:haolong="http://www.haolong.com/schema/user"
        xsi:schemaLocation="http://www.springframework.org/schema/beans

             http://www.springframework.org/schema/beans/spring-beans.xsd

             http://www.haolong.com/schema/user
             http://www.haolong.com/schema/user.xsd
             ">
     <haolong:user id="u" password="123" username="haolong"></haolong:user>
 </beans>

通过配置文件获取,成功!

Spring 中是如何完成的?

这里直接列举出最为底层的方法,方法如下:

 // 所属于的类:BeanDefinitionParserDelegate
 ​



 @Nullable
 public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
     // 1. 这个URI就是我们所说的命名空间的大名
     String namespaceUri = getNamespaceURI(ele);
     if (namespaceUri == null) {
         return null;
     }
     // 2. 读取Spring.handers 文件,然后根据URI,找到对应的 Handler
     NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
     if (handler == null) {
         error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
         return null;
     }
     // 3. 根据hander中的init方法,找到对应的Parse解析
     return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
 }

到这里位置,配置文件的解析阶段就已经全部完成了,这个时候将我们的图进行更新一下

life_3.png

至此,Spring的配置文件的解析阶段已经完成,最终得到了一个个BeanDefinition对象。这个时候仅是将XML解析出来,得到了一个个的BeanDefinition对象。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY6G77bV' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片