https://www.colabug.com/2539499.html

作为一个经常使用Spring的后端程序员,小编很早就想彻底弄懂整个Spring框架了!但它整体是非常大的,所有继承图非常复杂,加上小编修行尚浅,显得力不从心。不过,男儿在世当立志,今天就先从Spring IOC容器的初始化开始说起,即使完成不了对整个Spring框架的完全掌握,也不丢人,因为小编动手了,稳住,咱能赢!

下面说一些阅读前的建议:

  • 1、阅读源码分析是非常无聊的,但既然你进来了,肯定也是对这个东西进行了解,也希望这篇总结能对你有所启发。
  • 2、前方高能,文章可能会非常的长,图文并茂。
  • 3、阅读前建议你对相关设计模式、软件设计6大原则有所了解,小编会在行文中进行穿插。
  • 4、小编在读大四,学识尚浅,喜欢专研,如果你发现文章观点有所错误或者与你见解有差异,欢迎评论区指出和交流!
  • 5、建议你边看文章的时候可以边在IDE中进行调试跟踪
  • 6、文章所有UML图利用idea自动生成,具体生成方法为: 选中一个类名,先ctrl+shift+alt+U,再ctrl+alt+B,然后回车即可

二、文章将围绕什么来进行展开?

不多,就一行代码,如下图:

这句是Spring初始化的代码,虽然只有一句代码,但内容贼多!

三、Spring 容器IOC 有哪些东西组成?

这样子,小编先理清下思路,一步一步来:

  • 1、上面那句代码有个文件叫 applicationContext.xml ,这是个资源文件,由于我们的 bean都在里边进行配置定义,那Spring总得对这个文件进行 读取并解析 吧!所以Spring中有个模块叫 Resource 模块,顾名思义,就是 资源 嘛!用于对所有资源 xml、txt、property 等文件资源的抽象。关于对 Resource 的更多知识,可以参考下边两篇文章:

谈一谈我对Spring Resource的理解

Spring资源文件剖析和策略模式应用(李刚)

下面先贴一张小编生成的类图 (图片有点大,不知道会不会不清晰,如果不清晰可以按照上面说的idea生成方法去生成即可) :

可以看到 Resource 是整个体系的根接口,点进源码可以看到它定义了许多的 策略方法 ,因为它是用了 策略模式 这种设计模式,运用的好处就是 策略接口/类 定义了同一的策略,不同的子类有不同的具体策略实现,客户端调用时传入一个具体的实现对象 比如UrlResource或者FileSystemResource 给 策略接口/类Resource 即可!

所有 策略 如下:

  • 2、上面讲了Spring框架对各种资源的抽象采用了 策略模式 ,那么问题来了,现在表示资源的东西有了,那么是怎么把该资源加载进来呢?于是就有了下面的 ResourceLoader 组件,该组件负责对Spring资源的加载,资源指的是 xml 、 properties 等文件资源,返回一个对应类型的 Resource 对象。。UML图如下:

从上面的UML图可以看出, ResourceLoader 组件其实跟 Resource 组件差不多,都是一个根接口,对应有不同的子类实现,比如加载来自文件系统的资源,则可以使用 FileSystemResourceLoader ,加载来自 ServletContext 上下文的资源,则可以使用 ServletContextResourceLoader 。 还有最重要的一点,从上图看出, ApplicationContextAbstractApplication 是实现了 ResourceLoader 的,这说明什么呢?说明我们的应用上下文 ApplicationContext 拥有加载资源的能力,这也说明了为什么可以通过传入一个 String resource path 给 ClassPathXmlApplicationContext("applicationContext.xml") 就能获得xml文件资源的原因了!清晰了吗?nice!

  • 3、上面两点讲到了,好!既然我们拥有了加载器 ResourceLoader ,也拥有了对资源的描述 Resource ,但是我们在xml文件中声明的 标签在Spring又是怎么表示的呢?注意这里只是说对 bean 的定义,而不是说如何将 转换为 bean 对象。我想应该不难理解吧!就像你想表示一个学生 Student ,那么你在程序中肯定要声明一个类 Student 吧!至于学生数据是从 excel 导入,或者程序运行时 new 出来,或者从 xml 中加载进来这些都不重要,重要的是你要有一个将现实中的实体表示为程序中的对象的东西,所以 也需要在Spring中做一个定义!于是就引入一个叫 BeanDefinition 的组件,UML图如下:

下面讲解下UML图:

首先配置文件中的 标签跟我们的 BeanDefinition 是一一对应的, 元素标签拥有 class 、 scope、 lazy-init 等配置属性, BeanDefinition 则提供了相应的 beanClass 、 scope 、 lazyInit 属性。

其中 RootBeanDefinition 是最常用的实现类,它对应一般性的 元素标签, GenericBeanDefinition 是自 2.5 以后新加入的 bean 文件配置属性定义类,是一站式服务类。在配置文件中可以定义父 和子 ,父 用 RootBeanDefinition 表示,而子 用 ChildBeanDefiniton 表示,而没有父 的 就使用 RootBeanDefinition 表示。 AbstractBeanDefinition 对两者共同的类信息进行抽象。 Spring 通过 BeanDefinition 将配置文件中的 配置信息转换为容器的内部表示,并将这些 BeanDefiniton 注册到 BeanDefinitonRegistry 中。 Spring 容器的 BeanDefinitionRegistry 就像是 Spring 配置信息的内存数据库,主要是以 map 的形式保存,后续操作直接从 BeanDefinitionRegistry 中读取配置信息。一般情况下, BeanDefinition 只在容器启动时加载并解析,除非容器刷新或重启,这些信息不会发生变化,当然如果用户有特殊的需求,也可以通过编程的方式在运行期调整 BeanDefinition 的定义。

  • 4、有了加载器 ResourceLoader ,也拥有了对资源的描述 Resource ,也有了对 bean 的定义,我们不禁要问,我们的 Resource 资源是怎么转成我们的 BeanDefinition 的呢?因此就引入了 BeanDefinitionReader 组件,Reader嘛!就是一种读取机制,UML图如下:

从上面可以看出,Spring 对reader进行了抽象,具体的功能交给其子类去实现,不同的实现对应不同的类,如 PropertiedBeanDefinitionReader , XmlBeanDefinitionReader 对应从Property和xml的Resource解析成 BeanDefinition 。

其实这种读取数据转换成内部对象的,不仅仅是Spring专有的,比如:Dom4j解析器 SAXReader reader = new SAXReader(); Document doc = reader.read(url.getFile()); //url是一个URLResource对象 严格来说,都是Reader体系吧,就是将统一资源数据对象读取转换成相应内部对象。

  • 5、好了!基本上所有组件都快齐全了!对了,还有一个组件,你有了 BeanDefinition 后,你还必须将它们注册到工厂中去,所以当你使用 getBean() 方法时工厂才知道返回什么给你。还有一个问题,既然要保存注册这些 bean ,那肯定要有个数据结构充当容器吧!没错,就是一个 Map ,下面贴出 BeanDefinitionRegistry 的一个实现,叫 SimpleBeanDefinitionRegistry 的源码图:

BeanDefinitionRegistry 的UML图如下:

从图中可以看出, BeanDefinitionRegistry 有三个默认实现,分别是 SimpleBeanDefinitionRegistry , DefaultListableBeanFactory , GenericApplicationContext ,其中 SimpleBeanDefinitionRegistry , DefaultListableBeanFactory 都持有一个Map,也就是说这两个实现类把保存了bean。而 GenericApplicationContext 则持有一个 DefaultListableBeanFactory 对象引用用于获取里边对应的Map。 在 DefaultListableBeanFactory 中

在 GenericApplicationContext 中

  • 6、前面说的5个点基本上可以看出 ApplicationContext 上下文基本直接或间接贯穿所有的部分,因此我们一般称之为 容器 ,除此之外, ApplicationContext 还拥有除了 bean容器 这种角色外,还包括了获取整个程序运行的环境参数等信息(比如JDK版本,jre等),其实这部分Spring也做了对应的封装,称之为 Enviroment ,下面就跟着小编的eclipse,一起debug下容器的初始化工程吧!

四、实践是检验真理的唯一标准

学生类 Student.java 如下:

package com.wokao666;public class Student {private int id;private String name;private int age;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Student(int id, String name, int age) {super();this.id = id;this.name = name;this.age = age;}public Student() {super();}@Overridepublic String toString() {return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";}}

在 application.xml 中进行配置,两个 bean :

好了,接下来给最开头那段代码打个断点( Breakpoint ):

第一步: 急切地加载CONTEXTCLOSEDEVENT类,以避免在WEBLOGIC 8.1中的应用程序关闭时出现奇怪的类加载器问题。

这一步无需太过在意!

第二步: 既然是NEW CLASSPATHXMLAPPLICATIONCONTEXT() 那么就调用构造器嘛!

第三步:

第四步:

好,我们跟着第三步中的 super(parent) ,再结合上面第三节的第6小点UML图一步一步跟踪,然后我们来到 AbstractApplicationContext 的这个方法:

那么里边的 resourcePatternResolver 的类型是什么呢?属于第三节说的6大步骤的哪个部分呢?通过跟踪可以看到它的类型是 ResourcePatternResolver 类型的,而 ResourcePatternResolver 又是继承了 ResourceLoader 接口,因此属于加载资源模块,如果还不清晰,咱们再看看 ResourcePatternResolver 的源码即可,如下图:

对吧!不仅继承 ResourceLoader 接口,而且只定义一个 getResources() 方法用于返回 Resource[] 资源集合。再者,这个接口还使用了 策略模式 ,其具体的实现都在实现类当中,好吧!来看看UML图就知道了!

PathMatchingResourcePatternResolver 这个实现类呢!它就是用来解释不同路径资源的,比如你传入的资源路径有可能是一个常规的 url ,又或者有可能是以 classpath* 前缀,都交给它处理。

ServletContextResourcePatternResolver 这个实现类顾名思义就是用来加载 Servlet 上下文的,通常用在web中。

第五步:

接着第四步的方法,我们在未进入第四步的方法时,此时会对 AbstractApplicationContext 进行实例化,此时 this 对象的某些属性被初始化了 (如日志对象) ,如下图:

接着进入 getResourcePatternResolver() 方法:

第四步说了, PathMatchingResourcePatternResolver 用来处理不同的资源路径的,怎么处理,我们先进去看看!

如果找到,此时控制台会打印 找到用于OSGi包URL解析的Equinox FileLocator 日志。没打印很明显找不到!

运行完成返回 setParent() 方法。

第六步:

如果父代是非 null ,,则该父代与当前 this 应用上下文环境合并。显然这一步并没有做什么事! parent 显然是 null 的,那么就不合并嘛!还是使用当前 this 的环境。

做个总结:前六步基本上做了两件事:

  • 1、初始化相关上下文环境,也就是初始化 ClassPathXmlApplicationContext 实例
  • 2、获得一个 resourcePatternResolver 对象,方便第七步的资源解析成 Resource 对象

第七步:

第七步又回到刚开始第三步的代码,因为我们前面6步已经完成对 super(parent) 的追踪。让我们看看 setConfigLocation() 方法是怎么一回事~

/*** Set the config locations for this application context.//未应用上下文设置资源路径* 

If not set, the implementation may use a default as appropriate.//如果未设置,则实现可以根据需要使用默认值。 */ public void setConfigLocations(String... locations) { if (locations != null) {//非空 Assert.noNullElements(locations, "Config locations must not be null");//断言保证locations的每个元素都不为null this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim();//去空格,很好奇resolvePath做了什么事情? } } else { this.configLocations = null; } }

进入 resolvePath() 方法看看:

/*** 解析给定的资源路径,必要时用相应的环境属性值替换占位符,应用于资源路径配置。* Resolve the given path, replacing placeholders with corresponding* environment property values if necessary. Applied to config locations.* @param path the original file path* @return the resolved file path* @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)*/protected String resolvePath(String path) {return getEnvironment().resolveRequiredPlaceholders(path);}

进入 getEnvironment() 看看:

/*** {@inheritDoc}* 

If {@code null}, a new environment will be initialized via * {@link #createEnvironment()}. */ @Override public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = createEnvironment(); } return this.environment; }

进入 createEnvironment() ,方法,我们看到在这里创建了一个新的 StandardEnviroment 对象,它是 Environment 的实现类,表示容器运行的环境,比如JDK环境,Servlet环境,Spring环境等等,每个环境都有自己的配置数据,如 System.getProperties() 、 System.getenv() 等可以拿到JDK环境数据; ServletContext.getInitParameter() 可以拿到Servlet环境配置数据等等,也就是说Spring抽象了一个 Environment 来表示环境配置。

生成的 StandardEnviroment 对象并没有包含什么内容,只是一个标准的环境,所有的属性都是默认值。

总结:对传入的 path 进行路径解析

第八步: 这一步是重头戏

先做个小结:到现在为止,我们拥有了以下实例:

现在代码运行到如下图的 refresh() 方法:

看一下这个方法的内容是什么?

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 刷新前准备工作,包括设置启动时间,是否激活标识位,初始化属性源(property source)配置prepareRefresh();// 创建beanFactory(过程是根据xml为每个bean生成BeanDefinition并注册到生成的beanFactoryConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//准备创建好的beanFactory(给beanFactory设置ClassLoader,设置SpEL表达式解析器,设置类型转化器【能将xml String类型转成相应对象】,//增加内置ApplicationContextAwareProcessor对象,忽略各种Aware对象,注册各种内置的对账对象【BeanFactory,ApplicationContext】等,//注册AOP相关的一些东西,注册环境相关的一些beanprepareBeanFactory(beanFactory);try {// 模板方法,为容器某些子类扩展功能所用(工厂后处理器)这里可以参考BeanFactoryPostProcessor接口的postProcessBeanFactory方法postProcessBeanFactory(beanFactory);// 调用所有BeanFactoryPostProcessor注册为BeaninvokeBeanFactoryPostProcessors(beanFactory);// 注册所有实现了BeanPostProcessor接口的BeanregisterBeanPostProcessors(beanFactory);// 初始化MessageSource,和国际化相关initMessageSource();// 初始化容器事件传播器initApplicationEventMulticaster();// 调用容器子类某些特殊Bean的初始化,模板方法onRefresh();// 为事件传播器注册监听器registerListeners();// 初始化所有剩余的bean(普通bean)finishBeanFactoryInitialization(beanFactory);// 初始化容器的生命周期事件处理器,并发布容器的生命周期事件finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// 销毁已创建的beandestroyBeans();// 重置`active`标志cancelRefresh(ex);throw ex;}finally {//重置一些缓存resetCommonCaches();}}}

在这里我想说一下,这个 refresh() 方法其实是一个模板方法,很多方法都让不同的实现类去实现,但该类本身也实现了其中一些方法,并且这些已经实现的方法是不允许子类重写的,比如: prepareRefresh() 方法。更多模板方法设计模式,可看我之前的文章 谈一谈我对‘模板方法’设计模式的理解(Template)

先进入 prepareRefresh() 方法:

/*** Prepare this context for refreshing, setting its startup date and* active flag as well as performing any initialization of property sources.*/protected void prepareRefresh() {this.startupDate = System.currentTimeMillis();//设置容器启动时间this.closed.set(false);//容器关闭标志,是否关闭?this.active.set(true);//容器激活标志,是否激活?if (logger.isInfoEnabled()) {//运行到这里,控制台就会打印当前容器的信息logger.info("Refreshing " + this);}// 空方法,由子类覆盖实现,初始化容器上下文中的property文件initPropertySources();//验证标记为必需的所有属性均可解析,请参阅ConfigurablePropertyResolver#setRequiredPropertiesgetEnvironment().validateRequiredProperties();//允许收集早期的ApplicationEvents,一旦多播器可用,即可发布...this.earlyApplicationEvents = new LinkedHashSet();}

控制台输出:

三月 22, 2018 4:21:13 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@96532d6: startup date [Thu Mar 22 16:21:09 CST 2018]; root of context hierarchy

第九步:

进入 obtainFreshBeanFactory() 方法:

/*** 告诉子类刷新内部bean工厂(子类是指AbstractApplicationContext的子类,我们使用的是ClassPathXmlApplicationContext)* Tell the subclass to refresh the internal bean factory.*/protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory();//刷新Bean工厂,如果已经存在Bean工厂,那就关闭并销毁,再创建一个新的bean工厂ConfigurableListableBeanFactory beanFactory = getBeanFactory();//获取新创建的Bean工厂if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);//控制台打印}return beanFactory;}

进入 refreshBeanFactory() 方法:

/*** 该实现执行该上下文的基础Bean工厂的实际刷新,关闭以前的Bean工厂(如果有的话)以及为该上下文的生命周期的下一阶段初始化新鲜的Bean工厂。* This implementation performs an actual refresh of this context's underlying* bean factory, shutting down the previous bean factory (if any) and* initializing a fresh bean factory for the next phase of the context's lifecycle.*/@Overrideprotected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {//如果已有bean工厂destroyBeans();//销毁closeBeanFactory();//关闭}try {DefaultListableBeanFactory beanFactory = createBeanFactory();//创建一个新的bean工厂beanFactory.setSerializationId(getId());//为序列化目的指定一个id,如果需要,可以将此BeanFactory从此id反序列化回BeanFactory对象。//定制容器,设置启动参数(bean可覆盖、循环引用),开启注解自动装配customizeBeanFactory(beanFactory);将所有BeanDefinition载入beanFactory中,此处依旧是模板方法,具体由子类实现loadBeanDefinitions(beanFactory);//beanFactory同步赋值synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}

总结:这一步主要的工作就是判断刷新容器前是否已经有beanfactory存在,如果有,那么就销毁旧的beanfactory,那么就销毁掉并且创建一个新的beanfactory返回给容器,同时将xml文件的 BeanDefinition 注册到beanfactory中。 如果不太清楚可以回过头看看我们的第三节第5点内容

第十步:

进入第九步的 loadBeanDefinitions(beanFactory) 方法中去 take a look :

/*** 使用XmlBeanDefinitionReader来加载beandefnition,之前说过使用reader机制加载Resource资源变为BeanDefinition对象* Loads the bean definitions via an XmlBeanDefinitionReader.* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader* @see #initBeanDefinitionReader* @see #loadBeanDefinitions*/@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 创建XmlBeanDefinitionReader对象XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// 使用当前上下文Enviroment中的Resource配置beanDefinitionReader,因为beanDefinitionReader要将Resource解析成BeanDefinition嘛!beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));//初始化这个readerinitBeanDefinitionReader(beanDefinitionReader);//将beandefinition注册到工厂中(这一步就是将bean保存到Map中)loadBeanDefinitions(beanDefinitionReader);}

控制台输出:

三月 22, 2018 5:09:40 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [applicationContext.xml]

第十一步:

进入 prepareBeanFactory(beanFactory) 方法:

//设置bean类加载器
//设置Spring语言表达式(SpEL)解析器
//扫描ApplicationContextAware bean
//注册类加载期类型切面织入(AOP)LoadTimeWeaver
//为各种加载进入beanFactory的bean配置默认环境

第十二步:

postProcessBeanFactory(beanFactory) 方法:

postProcessBeanFactory 同样作为一个模板方法,由子类来提供具体的实现,子类可以有自己的特殊对 BeanDefinition 后处理方法,即子类可以在这对前面生成的 BeanDefinition ,即 bean的元数据再处理。比如修改某个 bean 的 id/name 属性、 scope 属性、 lazy-init 属性等。

第十三步:

invokeBeanFactoryPostProcessors(beanFactory) 方法:

该方法调用所有的 BeanFactoryPostProcessor ,它是一个接口,实现了此接口的类需重写 postProcessBeanFactory() 这个方法,可以看出该方法跟第十二步的方法是一样的,只不过作为接口,更多的是提供给开发者来对生成的 BeanDefinition 做处理,由开发者提供处理逻辑。

第十四步:

其余剩下的方法基本都是像 初始化消息处理源 , 初始化容器事件 , 注册bean监听器到事件传播器上 ,最后完成容器刷新。

五、总结

恭喜我,我终于写完了,同样也恭喜你,你也阅读完了。

我很佩服我自己能花这么长时间进行总结发布,之所以要进行总结,那是因为小编还是赞同 好记性不如烂笔头 的说法。

你不记,你过阵子就会忘记,你若记录,你过阵子也会忘记!区别在于忘记了,可以回过头在很短的时间内进行回忆,查漏补缺,减少学习成本。

再者,我认为我分析的还不是完美的,缺陷很多,因此我将我写的所有文章发布出来和大家探讨交流,汕头大学有校训说得非常地好,那就是说之知识是用来共享的,因为共享了,知识才能承前启后。

现在再梳理一下Spring初始化过程:

  • 1、首先初始化上下文,生成 ClassPathXmlApplicationContext 对象,在获取 resourcePatternResolver 对象将 xml 解析成 Resource 对象。
  • 2、利用1生成的context、resource初始化工厂,并将resource解析成beandefinition,再将beandefinition注册到beanfactory中。

朋友们,发现毛病,请评论告诉小编,一起交流一起交流!

Spring容器IOC初始化过程—今天终于进行总结了相关推荐

  1. idea中生成spring的 xml配置文件_【132期】面试再被问到Spring容器IOC初始化过程,就拿这篇文章砸他~...

    点击上方"Java面试题精选",关注公众号 面试刷图,查缺补漏 >>号外:往期面试题,10篇为一个单位归置到本公众号菜单栏->面试题,有需要的欢迎翻阅 阶段汇总集 ...

  2. 【132期】面试再被问到Spring容器IOC初始化过程,就拿这篇文章砸他~

    程序员的成长之路 互联网/程序员/技术/资料共享 关注 阅读本文大概需要 14 分钟. 作者:拥抱心中的梦想 juejin.im/post/5ab30714f265da237b21fbcc 一.老规矩 ...

  3. 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  4. Spring容器的初始化过程

    1.Spring 容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配号Bean之间的依 ...

  5. [Spring 深度解析]第7章 IoC容器的初始化过程

    7. IoC容器的初始化过程 ​ 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动.具体来说,这个启动包括BeanDefinition的Re ...

  6. Spring IoC(二)IoC容器的初始化过程

    (一)IoC 容器初始化过程概述 1.1简要概述初始化过程 IoC 容器的初始化过程是通过refresh() 方法来启动的,这个方法标识着IoC 容器正式启动.具体来说,这个启动过程包括:BeanDe ...

  7. 《Spring技术内幕》——2.3节IoC容器的初始化过程

    2.3 IoC容器的初始化过程 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动.具体来说,这个启动包括BeanDefinition的Res ...

  8. Spring IOC学习心得之IOC容器的初始化过程

    注:本文大多数内容都是摘自<Spring技术内幕>这本书 简单来说,Ioc容器的初始化过程是在refresh()方法中启动的,包括BeanDefinition的Resource定位,载入和 ...

  9. SpringBoot启动流程分析(四):IoC容器的初始化过程

    SpringBoot系列文章简介 SpringBoot源码阅读辅助篇: Spring IoC容器与应用上下文的设计与实现 SpringBoot启动流程源码分析: SpringBoot启动流程分析(一) ...

最新文章

  1. yum卸载遇到的问题--待解决
  2. vue.js----之router详解(三)
  3. 【转】Mybatis传多个参数(三种解决方案)
  4. javascript 自动填写表单
  5. 大数据之搭建HDP环境,以三个节点为例(上——部署主节点以及服务)
  6. 信号发送函数sigqueue和信号安装函数sigaction
  7. 赛码网编程题--打字(Java全A)
  8. 剧透和评析之車輪の国、向日葵の少女
  9. macOS Monterey 12.3.1 (21E258) 虚拟机 ISO 镜像
  10. Word样式窗格、模板格式
  11. (四)HEVC基本理论——变换单元TU
  12. STM32控制启动步进电机
  13. 文件夹正在使用,无法删除 无法重命名等操作怎么办?
  14. 蓝牙5.3 Core Spec演进与功能变化
  15. geoserver osm 导入_OSM导入PostGreSQL数据库 | 学步园
  16. win系统设置定时开机
  17. caffe入门学习:caffe.Classifier的使用
  18. NLP之语言词素Morpheme(形态学)
  19. linux qt 背景图片,《转》qt中添加背景图片(stylesheet)
  20. C++ primer 第十章 泛型算法

热门文章

  1. 程序员来聊一聊信用卡(四)——哪些银行的哪些卡容易办、值得办
  2. GoogleDeveloperDay 回顾
  3. shineblink ZE08K-CH2O甲醛测量
  4. 在Dash上使用d3.js
  5. mangoszero linux编译,(搬运工)MaNGOS-Zero编译注意事项
  6. uni-app IOS 音乐无法自动播放
  7. AK4191EQ+AK4499EX开发完成
  8. 自建机房跟IDC运营机房差异比较
  9. css去掉 ul li标签的前的点
  10. 三天打鱼,两天晒网(python)