扑街前言:本次内容的重点是BeanFactory 的创建、BeanDefinition 的构建以及配置文件的解析、还有Schema 机制分析(这里要结合dubbo 的一点点内容,对于dubbo 有一定了解的小伙伴可以看下,没有的话建议跳过,后面看dubbo 的时候在回来看这个内容)。

Spring源码下载

在源码分析之前,有一个前提就是你能看到源码,你得本地有源码。那么我们先说下怎么下载spring 的源码,这里和其余框架不同,目前spring 的源码下载后的项目依赖管理不是maven 而是Gradle。这里关于Gradle 的下载教程我就不细说了,网上有很多可以自己去找找,我这里重点要说的是通过GitHub 将spring 的源码下载下来之后,遇到的一些常见问题。对了这里还要下载git啊。

  1. 源码下载下来后,需要在bin目录下,执行 gradlew.bat(建议命令行中执行)。
  2. 需要在源码根目录下右击出Git Bash Here,点击后出现命令窗口(这个git下载了才会有)后用命令配置自己的GitHub 用户名、邮箱、密码等信息,具体命令我放在最后吧。这样能解决”process ‘command’ ‘git’ finishend with non-zero exit value“这样的报错。
  3. 还要注意Gradle 的版本不好是最新版本,我这边的版本是5.6.4版本,还有spring 中的gradle.properties 的文件,需要主要其中的版本version 信息需要保持一致。
  4. 最后一个就是如果使用idea 遇到了编码问题的话,可以在Help => Edit Custom VM Options,点击后再文件中添加“-Dfile.encoding=UTF-8”。

这样将代码导入基本上就没有什么问题,需要注意的是spring 目前要需要jdk 是jdk11 以上版本。注释版源码的话可以访问我的CSDN资源下载:源码


BeanFactory的创建

源码下载用idea加载后,我们可以先创建一个测试类,这个可以方便于我们后面debug。

public static void main(String[] args) {ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("classpath:applicationContext-cyclic.xml");TestService1 testService1 = (TestService1) applicationContext.getBean("testService1");TestService2 testService2 = (TestService2) applicationContext.getBean("testService2");testService1.aTest();testService2.aTest();
}

那么现在我们就可以启动后直接跟进ClassPathXmlApplicationContext 的构建里面,这个可以跟到的第一个代码点。这里只需要继续跟进核心方法。

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {// 1.如果已经有 ApplicationContext 并需要配置成父子关系,那么调用这个构造方法super(parent);// 2.根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)setConfigLocations(configLocations);// refresh值默认为trueif (refresh) {// *核心方法refresh();}
}

下面这个方法内容过多,可以直接根据文章目录点击到prepareRefresh方法,下面这段代码后面会一点一点解析

public void refresh() throws BeansException, IllegalStateException {// synchronized块锁(monitorenter --monitorexit)// 不然 refresh() 还没结束,又来个启动或销毁容器的操作//    startupShutdownMonitor就是个空对象,锁synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing./*【1.准备刷新】(1) 设置容器的启动时间(2) 设置活跃状态为true(3) 设置关闭状态为false(4) 获取Environment对象,校验配置文件(5) 准备监听器和事件的集合对象,默认为空的set集合*/prepareRefresh();// Tell the subclass to refresh the internal bean factory./*【2. 初始化 新BeanFactory】重点!(1)如果存在旧 BeanFactory,则销毁(2)创建新的 BeanFactory(DefaluListbaleBeanFactory)(3)解析xml/加载 Bean 定义、注册 Bean定义到beanFactory(不初始化)(4)返回新的 BeanFactory(DefaluListbaleBeanFactory)*/ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.// 【3. bean工厂前置操作】为BeanFactory配置容器特性// 例如类加载器、表达式解析器、注册默认环境bean、后置管理器BeanPostProcessorprepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.// 【4. bean工厂后置操作】此处为空方法,如果子类需要,自己去实现postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.//【5、调用bean工厂后置处理器】,开始调用我们自己实现的接口// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 回调方法invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.//【6. 注册bean后置处理器】只是注册,但是还不会调用//逻辑:找出所有实现BeanPostProcessor接口的类,分类、排序、注册registerBeanPostProcessors(beanFactory);// Initialize message source for this context.//【7、初始化消息源】国际化问题i18ninitMessageSource(); // ===> 就是往factory加了个single bean// Initialize event multicaster for this context.//8、【初始化事件广播器】初始化自定义的事件监听多路广播器// 如果需要发布事件,就调它的multicastEvent方法// 把事件广播给listeners,其实就是起一个线程来处理,把Event扔给listener处理// (可以通过 SimpleApplicationEventMulticaster的代码来验证)initApplicationEventMulticaster(); // ===> 同样,加了个bean// Initialize other special beans in specific context subclasses.// 9、【刷新:拓展方法】这是个protected空方法,交给具体的子类来实现//  可以在这里初始化一些特殊的 BeanonRefresh();// Check for listener beans and register them.//10、【注册监听器】,监听器需要实现 ApplicationListener 接口// 也就是扫描这些实现了接口的类,给他放进广播器的列表中// 其实就是个观察者模式,广播器接到事件的调用时,去循环listeners列表,// 挨个调它们的onApplicationEvent方法,把event扔给它们。registerListeners();  // ===> 观察者模式// Instantiate all remaining (non-lazy-init) singletons.//11、 【实例化所有剩余的(非惰性初始化)单例】// (1)初始化所有的 singleton beans,反射生成对象/填充// (2)调用Bean的前置处理器和后置处理器finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.// 12、【结束refresh操作】// 发布事件与清除上下文环境finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}

进入核心方法,这里我们就需要一个一个方法看了,不过这次我们只看到BeanFactory 的创建和配置文件的解析。

prepareRefresh方法

首选我们可以看到的就是prepareRefresh 方法,这个方法其主要作用就是如下5点:

  1. 设置容器的启动时间
  2. 设置活跃状态为true
  3. 设置关闭状态为false
  4. 获取Environment对象,校验配置文件
  5. 准备监听器和事件的集合对象,默认为空的set集合

其实主要就是做一些准备工作,具体代码可以自己去翻一下,我这里就不看了。

obtainFreshBeanFactory-构建BeanFactory的方法

这方法是我们本次的重点,其主要做用也就是如下4点,这里我们需要一点点看。

/*【2. 初始化 新BeanFactory】重点!(1)如果存在旧 BeanFactory,则销毁(2)创建新的 BeanFactory(DefaluListbaleBeanFactory)(3)解析xml/加载 Bean 定义、注册 Bean定义到beanFactory(不初始化)(4)返回新的 BeanFactory(DefaluListbaleBeanFactory)
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

跟进该方法可以看到是主要调用refreshBeanFactory 和getBeanFactory 方法,这里的重点就是前者方法,后者只是将前者设置到的对象进行一个返回。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 1.关闭旧的 BeanFactory (如果有),创建新的 BeanFactoryrefreshBeanFactory();// 2.返回刚创建的 BeanFactory(ConfigurableListableBeanFactory)return getBeanFactory();
}

过度方法-refreshBeanFactory方法等等

跟进后我们来到的是AbstractRefreshableApplicationContext 类中,目前方法第一步就是先判断BeanFactory 是否存在,因为容器中只能存在一个BeanFactory 对象,存在则销毁,然后在创建一个全新的BeanFactory 对象,然后设置一些默认属性,然后就是继续方法调用,最后返回创建好的对象,这里的重点又是下一步方法loadBeanDefinitions。

@Override
protected final void refreshBeanFactory() throws BeansException {// 1.判断是否已经存在 BeanFactory,如果存在则先销毁、关闭该 BeanFactoryif (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {// 2.创建一个新的BeanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();// 设置标识,用于 BeanFactory 的序列化beanFactory.setSerializationId(getId());// 设置 BeanFactory 的两个配置属性:(1)是否允许 Bean 覆盖 (2)是否允许循环引用customizeBeanFactory(beanFactory);/*重点-->:加载 Bean 到 BeanFactory 中1、通过BeanDefinitionReader解析xml为Document2、将Document注册到BeanFactory 中(这时候只是bean的一些定义,还未初始化)*/// 3.加载 bean 定义loadBeanDefinitions(beanFactory);this.beanFactory = beanFactory;}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}
}

因为我这里使用的xml 方式进行的配置,所以我进入的是AbstractXmlApplicationContext 类,如果是注解的话进入的就是AnnotationConfigWebApplicationContext 类。

这里基本工作就是获取一些资源解析器,用于后面的解析xml 配置信息,重点跟进的地方任然是loadBeanDefinitions 方法

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// Create a new XmlBeanDefinitionReader for the given BeanFactory.// 1.为指定BeanFactory创建XmlBeanDefinitionReaderXmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// Configure the bean definition reader with this context's// resource loading environment.// 2.使用此上下文的资源加载环境配置 XmlBeanDefinitionReaderbeanDefinitionReader.setEnvironment(this.getEnvironment());// 资源加载器beanDefinitionReader.setResourceLoader(this);// 实体解析器beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// Allow a subclass to provide custom initialization of the reader,// then proceed with actually loading the bean definitions.// 校验,配置文件xsd和dtd头部initBeanDefinitionReader(beanDefinitionReader);// 3.加载 bean 定义 **===》loadBeanDefinitions(beanDefinitionReader);
}

依然在AbstractXmlApplicationContext 类中,这里调用进入的方法都是同一个,只有获取资源的路径的方式不一样而已,还是需要跟进loadBeanDefinitions 方法,不过这次是AbstractBeanDefinitionReader 类中。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {//下面的if分支一般会走第2个,无论走哪个,if里面的调的方法都是load// 分支1::获取ResourceResource[] configResources = getConfigResources();if (configResources != null) {reader.loadBeanDefinitions(configResources);}// 分支2:获取资源的路径//前面解析出来的那些配置文件,classpath*:application.xmlString[] configLocations = getConfigLocations();if (configLocations != null) {//重要!解析xml的结构正是在这里开端!!!reader.loadBeanDefinitions(configLocations);}
}

继续看就是还是跟进,这里的作用就是一个计数,这里会根据不同的配置文件重复调用loadBeanDefinitions。

这里下一步还是AbstractBeanDefinitionReader 类的loadBeanDefinitions 重载方法,不过这里任然是调用loadBeanDefinitions 方法。

最后的我们达到的是XmlBeanDefinitionReader 类的loadBeanDefinitions 方法,然后紧接着是调用其doLoadBeanDefinitions 方法。

@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {Assert.notNull(locations, "Location array must not be null");// 计数,来统计所有xml里一共有多少个beanint count = 0;for (String location : locations) {//继续进入loadBeanDefinitions,解析xml文件count += loadBeanDefinitions(location);}//最终返回的classpath*:application.xml中配置bean的个数return count;
}

在这里我们会将xml 使用inputSource和resource加载,并封装成Document 对象。然后传入registerBeanDefinitions 方法,这里还有一堆的异常分析捕捉。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {// 1.根据inputSource和resource加载XML文件,并封装成DocumentDocument doc = doLoadDocument(inputSource, resource);// 2.根据返回的Document注册Bean信息(对配置文件的解析,核心逻辑) ====>int count = registerBeanDefinitions(doc, resource);if (logger.isDebugEnabled()) {logger.debug("Loaded " + count + " bean definitions from " + resource);}return count;}catch (BeanDefinitionStoreException ex) {throw ex;}
}

到了这里我们就已经快接近真正的解析了,我们可以直接看下面代码中的第3步BeanDefinitionDocumentReader 对象的registerBeanDefinitions 方法调用。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {// 1.获取documentReader,用于读取通过xml获得的documentBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();// 2.获取当前xml文件(document)解析前,已经注册的BeanDefinition数目int countBefore = getRegistry().getBeanDefinitionCount();// 3.解析并注册当前配置文件中的BeanDefinition =====》进入documentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 4.用当前注册的BeanDefinition数目减去之前注册的数目,返回该配置文件中注册BeanDefinition数目return getRegistry().getBeanDefinitionCount() - countBefore;
}

进入后就是doRegisterBeanDefinitions 方法的调用。这里我们依旧是看重点parseBeanDefinitions 方法。

protected void doRegisterBeanDefinitions(Element root) {/*我们看名字就知道,BeanDefinitionParserDelegate 必定是一个重要的类,它负责解析 Bean 定义,这里为什么要定义一个 parent? 看到后面就知道了,是递归问题,因为 <beans /> 内部是可以定义 <beans /> 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 <beans /> 节点,从源码分析的角度,我们当做根节点就好了*/BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);// 1.校验root节点的命名空间是否为默认的命名空间(默认命名空间http://www.springframework.org/schema/beans)if (this.delegate.isDefaultNamespace(root)) {// 2.处理profile属性//<beans ... profile="dev" />String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {//正常情况不会进入这里,具体代码就不展示了}}}/// 3.解析前处理, 留给子类实现preProcessXml(root);// 4.解析并注册bean定义, 核心解析方法,解析各种xml的标签,注册到BeanFactory!parseBeanDefinitions(root, this.delegate);   //====》// 5.解析后处理, 留给子类实现postProcessXml(root);this.delegate = parent;
}

总结一下上面的过度方法:就是将原本的配置信息,转换封装为方便解读的对象,然后准备初始化一系类的解析器,并对返回对象进行封装等等。这里也看出xml 配置和注解配置,也就是这里会有一定的区别。

解析配置信息-parseBeanDefinitions方法

继续到了这一步,我们就算到了真正解析xml配置文件的地方了,这里还会涉及到后面的Schema 机制分析。

这里一共是两种解析方式:parseDefaultElement 默认命名空间默认节点的处理,比如常规的bean 标签等等、parseCustomElement 自定义命名空间自定义节点的处理,比如dubbo 提供的dubbo:service 标签等或者我们自定的标签,还有就是spring 本身的一些标签比如aop 标签等,这里其实也就是Schema 机制的解析方式。

/*** //解析beans下的xml节点,调用不同的方法来处理不同的节点* @param root* @param delegate*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// 遍历root的子节点列表// default namespace 涉及到的就四个标签 <import />、<alias />、<bean /> 和 <beans />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)) {// 1.1 默认命名空间节点的处理,例如: <bean id="test" class="" />//分支1:代表解析标准元素 <import />、<alias />、<bean />、<beans /> 这几个//标准节点parseDefaultElement(ele, delegate);}else {// 1.2 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>//分支2:代表解析 <mvc />、<task />、<context />、<aop /> 、<component-scan />等//特殊节点delegate.parseCustomElement(ele);}}}}else {// 2.自定义命名空间的处理delegate.parseCustomElement(root);}
}
默认命名空间的解析方式-parseDefaultElement

我们就先看parseDefaultElement 的解析方式,首选是对各种默认标签进行分类处理,我们只看bean 标签的处理方式就好。

/*** 标签解析* @param ele* @param delegate*/
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {// 1.对import标签的处理if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {// <import resource="classpath:applicationContext-datasource.xml" />importBeanDefinitionResource(ele);}// 2.对alias标签的处理else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {// <alias name="abc" alias="af"/>processAliasRegistration(ele);}// 3.对bean标签的处理(最复杂最重要)else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {// 处理 <bean /> 标签定义 ====》processBeanDefinition(ele, delegate);}// 4.对beans标签的处理else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// 如果碰到的是嵌套的 <beans /> 标签,需要递归doRegisterBeanDefinitions(ele);}
}
Bean标签的解析方法-processBeanDefinition

这里首先就是通过parseBeanDefinitionElement 方法解析标签中的所有内容,然后构建出BeanDefinition 这个对象,这个对象中存储的就是具体的配置信息,比如bean 的id、name、class 等属性,代码中虽然是BeanDefinitionHolder 但是它本身也就是对BeanDefinition 的一个封装而已。这里具体的解析标签的方法就详细看了,因为大部分跟市面上的解析方式都差不多,MyBatis 好像也就是一个套路。

这里的第二个重点就是registerBeanDefinition 注册方法,这个说直白一点就是为DefaultListableBeanFactory 对象,也就是往上面创建的BeanFactory 对象的beanDefinitionMap 集合添加对应的BeanDefinition 值,注意的是BeanDefinition 是一个ConcurrentHashMap 集合,并且这里是私有常量。

到这里Bean 的解析工作就算是结束了,同时也验证了上篇文章所有的是先创建了BeanFactory 对象,然后再解析构建的BeanDefinition 对象,最后又将BeanDefinition 对象存入了BeanFactory 对象中。

/*** 两步:1、解析,封装到对象的属性上去,*      2、注册,注册到factory里去(其实就是俩Map存起来)*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {// 1.bean节点具体的解析过程,属性,值等(bdHolder会包含一个Bean节点的所有属性,例如name、class、id)  ====》BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {// 2.若存在默认标签的子节点下再有自定义属性,需要再次对自定义标签再进行解析(基本不用,不做深入解析)bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.//3.注册BeanBeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// Send registration event.// 4.注册完成后,发送事件getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}
}

Schema 机制分析

结合上面的解析配置信息的内容,我们在这里简单的说一下Schema 机制,这里需要用到dubbo 的一点知识。

下面就是dubbo 的常用配置xml,结合刚刚上面的内容,spring 启动的时候是只能解析下面的配置的bean 标签,对于dubbo 标签是没有解析的,但是上面内容也提到自义定命名空间和标签,也是可以通过parseCustomElement 方法来进行解析的,而这段解析内容也可以算是schema 机制的重点。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsdhttp://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"><!-- provider's application name, used for tracing dependency relationship --><dubbo:application name="demo-provider"  /><!-- <dubbo:metadata-report address="zookeeper://192.168.200.129:2181"/>--><dubbo:registry address="zookeeper://127.0.0.1:2181" /><!--<dubbo:provider export="true" scope="remote" />--><!-- use dubbo protocol to export service on port 20880 --><dubbo:protocol name="dubbo"  /><!-- service implementation, as same as regular local bean --><bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/><!-- declare the service interface to be exported --><dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/><bean id="myDemoService" class="org.apache.dubbo.demo.provider.MyDemoServiceImpl" /><dubbo:service interface="org.apache.dubbo.demo.MyDemoService" ref="myDemoService"/>
</beans>

自定义命名空间和标签

我们先要了解的是怎样才算是自定义命名空间,其实我们将上面的xml 配置和spring 常规xml 配置对比,就能发现beans 标签中多了一个xmlns:dubbo 的属性,而xsi:schemaLocation 属性中也多出了一段关于dubbo 的dubbo.xsd 配置。

这两个东西具体作用就是:dubbo.xsd 文件规定了dubbo 标签的语法;而http://dubbo.apache.org/schema/dubbo 对应着META-INF 目录下spring.handlers 文件中配置的解析器类的路径。dubbo.xsd 文件也是在这个目录下的spring.schemas 文件导向的。

自定义命名空间和标签的解析-parseCustomElement方法

既然知道了相关的语法和对象的解析器,那么我们就可以回到parseCustomElement 方法看看具体的解析内容,直接可以定位到BeanDefinitionParserDelegate 类的parseCustomElement 方法。

我们这里重点看两个地方,其一是:得到一个命名空间处理器,也就是resolve 方法、其二就是:开始解析,也就是parse 方法。

@Nullable
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));
}

先看resolve 方法,这里我们其实要看的就是三个部分,初始化、缓存、返回,重点就是初始化。

这里可能还有人有疑惑,到底是怎么得到解析器的,其实就是spring 中的约定,约定好schema 就是通过spring.handlers 文件得到解析器,spring.schemas 文件规定语法,然后spring 项目中全部这个命名的文件内容,然后将信息存储下来。

既然我们上面已经得到了整个dubbo 的xml 配置文件,那么也就是得到了先关的beans 标签的值,这里就可以通过http://dubbo.apache.org/schema/dubbo 获取到相关的解析器,也就是DubboNamespaceHandler 对象,这个对象的顶层父类也就是NamespaceHandler 对象。

NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;

然后就是DubboNamespaceHandler 对象的init 初始化方法。这里的初始化写法是固定的,又spring 提供,这可以看到是将各个不同标签的解析对象都创建,并封装成了DubboBeanDefinitionParser 对象,那么后面调用的话只是调用了DubboBeanDefinitionParser 对象的parse 方法,而不是对应的解析器的parse 方法,比如service 标签对应的ServiceBean 解析器。

@Override
public void init() {/*** 解析配置文件中<dubbo:xxx></dubbo:xxx> 相关的配置,并向容器中注册bean信息*/registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}

DubboBeanDefinitionParser 对象的parse 方法的调用,这里则是通过不同的标签来构建出真正的解析器,比如service 标签对应的ServiceBean 解析器。

而解析器的真正调用,也就是真正的标签解析,我们后面说。这里解析器逻辑只针对dubbo 啊,其余的不同框架都有一定的差异,但是大体流程就是这样的。


总结

本次的内容的重点就是BeanFactory 的创建流程、BeanDefinition 的构建和存储、缓存,配置信息的解析。结合之前的流程图,这也是完成对象实例化和初始化之前的准备工作,目前BeanFactory 的单例池是没有任何配置对象的,添加对象那是后面的事情。下一篇就是BeanFactoryPostProcessor 和BeanPostProcessor 的区别了。


附录Spring 源码分析系列文章

时间 文章
2022-03-09 Spring的基本概念和IOC流程的简述
2022-03-11 IOC流程解析-BeanFactory的创建
2022-03-14 IOC流程解析-BeanFactoyPostProcessor和BeanPostProcessor
2022-03-15
2022-03-17

IOC流程解析-BeanFactory的创建相关推荐

  1. KafkaController创建topic流程解析

    一.kafka-topic.sh 为了便于操作Kafka集群,Kafka源码包中提供了多个shell脚本,其中kafka-topic.sh提供了Topic的创建.修改.列举.描述.删除功能,内部通过T ...

  2. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  3. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  4. SpringMVC 执行流程解析

    SpringMVC 执行流程解析 注:SpringMVC 版本 5.2.15 上面这张图许多人都看过,本文试图从源码的角度带大家分析一下该过程. 1. ContextLoaderListener 首先 ...

  5. SpringBoot2 | Spring IOC 流程中核心扩展接口的12个扩展点源码分析(十一)

    微信公众号:吉姆餐厅ak 学习更多源码知识,欢迎关注. SpringBoot2 | SpringBoot启动流程源码分析(一) SpringBoot2 | SpringBoot启动流程源码分析(二) ...

  6. java中ioc作用_Spring中的IOC是什么意思,为什么要用IOC而不是New来创建实例?

    这里是修真院后端小课堂,每篇分享文从 [背景介绍][知识剖析][常见问题][解决方案][编码实战][扩展思考][更多讨论][参考文献] 八个方面深度解析后端知识/技能,本篇分享的是: [Spring中 ...

  7. Spring IOC BeanDefinition解析

    Spring IOC BeanDefinition解析 IOC(Inversion of Control)即控制反转,是说创建对象的控制权进行了转移,以前创建对象的主动权和创建时机是由自己把控的,而现 ...

  8. 深入浅出Spring源码:IOC原理解析(一)

    IOC(Inversion of Control),即控制反转,意思是将对象的创建和依赖关系交给第三方容器处理,我们要用的时候告诉容器我们需要什么然后直接去拿就行了.举个例子,我们有一个工厂,它生产各 ...

  9. 基于神策用户画像,在线教育企业线索标签体系搭建及培育全流程解析

    作者介绍:TigerHu,环球网校大数据营销产品 leader,主导数据产品线和营销 CRM 产品线. 本文内容均从作者真实实践过程出发,结合作者公司与神策数据合作真实场景,从神策用户画像产品出发,全 ...

  10. Android-Multidex安装流程解析

    Android-Multidex安装流程解析 关于为什么需要引入Multidex支持以及如何配置Multidex可参考官网,本篇不做阐述,本篇着重分析Multidex1.0.2源码进行分析 大家都知道 ...

最新文章

  1. 首先声明两者所要实现的功能是一致的(将多维数组降位一维)。这点从两个单词的意也可以看出来,ravel(散开,解开),flatten(变平)。两者的区别在于返回拷贝(copy)还是返回视图(view)
  2. 虚拟机centos7忘记密码怎么办_电脑忘记密码怎么办
  3. 详细解读Volley(二)—— ImageRequest Request简介
  4. Docker Swarm删除节点
  5. centos7双网卡,一个连公网,一个连内网
  6. 算术基本定理(维基百科)
  7. OD 快捷键使用大全。非常详细( 游戏逆向分析必看 )+ OD 断点 使用大全
  8. cocos creator 方法数组_Creator | 优化三剑客之内存!
  9. 内核中的UDP socket流程(3)(4)——sock_create
  10. Ubuntu18.04安装配置Qt5.15
  11. 2.6 列昂惕夫投入产出模型(第2章矩阵代数)
  12. php最小二乘法,Python中实现最小二乘法思路及实现代码
  13. Hi3861 ADC驱动 如何使用开发板上的3个按键
  14. Java 实现发票信息输出为 PDF 文件(模板化)
  15. POST /product/:id 获取单个商品
  16. cython编译python_Cython编译python为so 代码加密示例
  17. cfDNA的5-羟甲基胞嘧啶谱高度预测弥漫大B细胞淋巴瘤患者的R-CHOP治疗反应
  18. MapReduce系列之MapReduce的输入
  19. HTML Url 编码(Encode 和 Url Decode)
  20. 设计模式之代理模式、动态代理模式、Cglib代理模式

热门文章

  1. 数显之家快讯:【SHIO世硕心语】2021,写给自己的几段宽心话!
  2. Linux网络适配器不见了,linux – lspci未显示HyperV网络适配器
  3. STM32仿真器下载配置
  4. 安路TD和modelsim仿真
  5. httclient出现的entity consumed
  6. 雷顿学院大数据一期课程免费下载
  7. 基于深度学习的语义分割
  8. camera-radar fusinon paper 速递
  9. 【shiro基础】springboot + shiro整合thymeleaf
  10. krait和kryo_高效的Java序列化(Kryo和FST)