spring底层源码分析-IOC容器的初始化过程
文章目录
1、IOC容器的初始化过程
完成初始化可以由以下几行代码完成:
ClassPathResource res = new ClassPathResource("beans.xml"); //找到xml配置文件
//DefaultListableBeanFactory是IOC容器的具体实现,内含currentHashMap,实现了BeanDefinitionRegistry接口。
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//xml的解析器。
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
//解析xml,并将解析得到的bean对象注册到factory中
reader.loadBeanDefinitions(res);
//后面就可以:factory.getBean("user"); 了
1.1 定位
定位是找到xml配置文件。
定位功能主要是Resource接口,他的具体实现类都有定位功能。
1.2 解析和载入
从FileSystemXmlApplicationContext入手。
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("beans.xml");
然后会调用refresh(),这个方法很重要,每个具体的applicationContext都会调用,refresh()是在父类AbstractApplicationContext实现的。
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
//obtainFreshBeanFactory()启动子类的refreshBeanFactory(),首先清除原有的IOC容器,然后创建DefaultListableBeanFactory容器,并进行Bean的定位、载入和解析。
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
//空方法,由子类实现
this.postProcessBeanFactory(beanFactory);
//实例化并执行所有的BeanFactoryPostProcessors
this.invokeBeanFactoryPostProcessors(beanFactory);
//实例化并注册所有的BeanPostProcessors
this.registerBeanPostProcessors(beanFactory);
//国际化
this.initMessageSource();
//初始化事件广播器
this.initApplicationEventMulticaster();
//空方法,由子类实现
this.onRefresh();
//注册监听器
this.registerListeners();
//初始化剩余的非懒加载的单例对象
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
1.2.1 obtainFreshBeanFactory()方法:
定位及载入过程:
大致脑图,(具体过程看下面代码)解析过程看下一个篇章:
//AbstractApplicationContext的obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
return this.getBeanFactory();
}
//子类的AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException {
//判断是否已经有IOC容器了,有则清除容器
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
//创建DefaultListableBeanFactory容器
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
//进行Resource定位、XmlBeanDefinitionReader载入以及loadBeanDefinitions(XmlBeanDefinitionReader)解析,具体代码在下面:
this.loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
} catch (IOException var2) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
}
}
//子类AbstractXmlApplicationContext的loadBeanDefinitions
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException {
//这里使用了XmlBeanDefinitionReader,如果不是FileSystemXmlApplicationContext,则applicationcontext则会使用其他类型的BeanDefinitionReader。
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
//为XmlBeanDefinitionReader配置ResourceLoader,因为该类的父类是DefaultResourceLoader,所以this可以直接使用
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
//这里启动Bean的信息载入和解析,具体代码如下:
this.loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//以Resource方式获得配置文件的资源位置
Resource[] configResources = this.getConfigResources();
if (configResources != null) {
//具体代码在下面。
reader.loadBeanDefinitions(configResources);
}
//以String形式获得配置文件的位置
String[] configLocations = this.getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
//AbstractBeanDefinitionReader类,是XmlBeanDefinitionReader的父类
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
//如果Resource为null,停止载入
Assert.notNull(resources, "Resource array must not be null");
//用于计数:成功载入多少个配置文件。
int count = 0;
for (Resource resource : resources) {
//loadBeanDefinitions(resource)是载入配置文件,具体代码在下面。
count += loadBeanDefinitions(resource);
}
return count;
}
//XmlBeanDefinitionReader类
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//具体代码在下面。
return loadBeanDefinitions(new EncodedResource(resource));
}
//这里是载入xml形式的BeanDefinition的地方
public int loadBeanDefinitions(EncodedResource encodedResource)
throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
//这里得到XML文件,使用InputStream准备读取
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//具体的读取过程在这个函数,具体代码在后面。
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//这里取得XML文件的Document对象,这是通用的解析过程,这些document对象没有按照Spring的Bean规则进行解析
Document doc = doLoadDocument(inputSource, resource);
//registerBeanDefinitions是解析过程,会使用到Spring的Bean配置规则,具体请看下面的解析篇章。
//registerBeanDefinitions对载入的Bean的数量进行了统计。
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
解析过程:
所谓bean的解析就是将我们的xml文件中的bean解析出来。
大致脑图:(具体看代码)
registerBeanDefinitions(Document doc, Resource resource)方法:
public int registerBeanDefinitions(Document doc, Resource resource)
throws BeanDefinitionStoreException {
//BeanDefinitionDocumentReader来对xml的BeanDefination进行解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获得之前已经解析了的Bean的数量.
int countBefore = getRegistry().getBeanDefinitionCount();
//具体解析过程在这个registerBeanDefinitions中完成,具体代码如下。
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//DefaultBeanDefinitionDocumentReader类的方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//这些if与else不知道干什么的,直接看后面。
if (this.delegate.isDefaultNamespace(root)) {
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);
//这个是重点方法,内有processBeanDefinition方法,分析xml配置文件,将Document封装成为BeanDefinitionHolder(这个是BeanDefinition的实现类,用来装一个个的Bean以及对应属性),然后把BeanDefinitionHolder装入到DefaultListableBeanFactory的currentHashMap上,同时也有键值对(别名,实际名称)装入到别名树(也是currentHashMap)。delegate是个委托类,具体的处理委托给了delegate(BeanDefinitionParserDelegate类),主要在processBeanDefinition方法中操作。
//具体代码在下面
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
//直接看processBeanDefinition方法
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)) {
//processBeanDefinition方法在这里,直接看这个就行了。
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//IMPORT_ELEMENT对应xml中的<import>
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//ALIAS_ELEMENT对应xml中的alias,也就是别名
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
//别名注册
processAliasRegistration(ele);
}
//BEAN_ELEMENT对应xml中的bean。
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
//bean对象注册,具体代码在后面。
processBeanDefinition(ele, delegate);
}
//NESTED_BEANS_ELEMENT对应xml中的beans。
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
doRegisterBeanDefinitions(ele);
}
}
//这里就是处理BeanDefinition的地方,具体处理委托给BeanDefinitionParserDelegate类。Element对应xml中的元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//获得BeanDefinition的实现类BeanDefinitionHolder,里面有Bean对象以及别名以及属性等。得到这个BeanDefinitionHolder说明对xml元素的解析是按照Spring Bean规则的。
//想看xml元素的解析可以自行去看parseBeanDefinitionElement这个方法。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//这里是向DefaultListableBeanFactory的currentHashMap注册bean,也就是向IOC容器注册。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//在IOC容器注册完后,发送信息,现在暂时不理解为什么。
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
**总结:**BeanDefinitionHolder持有bean对象以及别名集合以及其他属性(多例单例,是否懒加载属性等),BeanDefinitionHolder的生成是通过Document文档树的内容进行解析得到的,这个解析过程是由BeanDefinitionParserDelegate来实现(具体在parseBeanDefinitionElement方法中实现),最后把BeanDefinitionHolder注册到DefaultListableBeanFactory这个IOC容器中。
现在在IOC容器中存在的还只是一些静态的配置信息,严格来说,这个时候的容器还没有完全起作用,要完全发挥容器的作用,还需完成数据向容器的注册。
1.3 IOC容器的注册
前面已经分析过BeanDefinition在IOC容器中载入和解析过程。在这些动作完成后,用户定义的BeanDefinition信息已经在IOC容器内建立起了自己的数据结构已经相应的数据表示,但这些数据还不能提供IOC容器直接使用,需要在IOC容器中对这些BeanDefinition数据进行注册。
在IOC容器的DefaultListableBeanFactory中是通过一个HashMap来持有载入的BeanDefinition的。在DefaultListableBeanFactory定义可以看到,如下:
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
将解析得到的BeanDefinition向IOC容器中的beanDefinitionMap注册的过程是在载入BeanDefinition完成后进行的,注册的调用过程图下:
在DefaultListableBeanFactory中实现了BeanDefinitionRegistry接口,这个接口实现完成BeanDefinition向容器的注册。这个注册不复杂,就是把解析得到的BeanDefinition设置到HashMap中去。
//这个方法是在DefaultListableBeanFactory中
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);
}
}
//获得beanDefinitionMap的key为beanName的对象
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) { //如果不为空,说明之前已经创建了key为beanName的BeanDefinition
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 + "]");
}
}
//将原来的BeanDefinition覆盖掉
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else { //如果为空
if (hasBeanCreationStarted()) { //如果已经启动,我的理解:就是这个IOC容器已经投入使用了。
synchronized (this.beanDefinitionMap) {
//我的意思:因为IOC容器已经投入使用了,使用期间可以有多个线程添加新的BeanDefinition对象,所以要在使用期间添加新的BeanDefinition对象需要加锁,updatedDefinitions是ArrayList类,是线程不安全。防止多个线程添加造成数据不一致。
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 { //如果IOC容器仍在注册阶段。我的意思:只有单线程,所以不需要加锁。
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();
}
}
通过注册阶段,把BeanDefinition加入到DefaultListableBeanFactory的beanDefinitionMap中。IOC容器建立整个Bean的配置信息,容器的作用就是对zhe’x信息进行处理和维护。这些信息是容器建立依赖反转的基础,所以下面将介绍依赖注入。
1.4 IOC容器的依赖注入
假设当前IOD容器已经载入了用户定义的信息,开始分析依赖注入原理。
单例对象存放的地方:
DefaultSingletonBeanRegistry类中:
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
因为DefaultListableBeanFactory类继承AbstractBeanFactory,而AbstractBeanFactory继承DefaultSingletonBeanRegistry。所以DefaultListableBeanFactory有singletonObjects这个HashMap。
非懒加载的单例对象的依赖注入的过程是创建IOC容器并初始化对象时触发。
在BeanDefinition信息中通过控制 lazy-init 属性为true,则初始化对象在用户第一次向IOC容器索要的触发(同理,依赖注入也是在这个时候)。
在IOC容器的BeanFactory中,有一个getBean的接口定义,这个接口的实现就是触发依赖注入发生的地方。
太过复杂,以后需要慢慢琢磨。笔记还不会做…
1.5 IOC容器的三级缓存
DefaultSingletonBeanRegistry类中:
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); //一级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); //三级缓存
private final Map<String, Object> earlySingletonObjects = new HashMap(16); //二级缓存
区别:
singletonObjects 存的是完整对象;earlySingletonObjects存的是实例化但还未初始化的对象;singletonFactories存的是 ObjectFactory<?>对象,也就是lambd表达式,程序在从三级缓存获取对象时会执行这个表达式从而产生对象。
三级缓存解决循环依赖:
假设类A的对象a,类B的对象b,a和b之间循环依赖,那么三级缓存解决循环依赖的步骤如下:
- 先从一级缓冲获取A单例,A单例不存在一级缓存且不在构造中(不在构造中也就是未开始实例化),则实例化a对象,然后将a对象存入三级缓存(实际存入三级缓存的是lambd表达式,这里为了易于理解所以描述为a对象),然后依赖注入a对象里的属性时,发现属性里的引用B是引用类型。
- 通过引用B这个属性,去一级缓存获取B单例,B单例不存在且不在构造中,则开始实例化b对象,然后将b对象存入三级缓存(也是lambd表达式),然后依赖注入b对象里的属性时,发现属性里的引用A时应用类型。
- 通过引用A这个属性,去一级缓存获取A单例(也就是a对象),A单例不存在但在构造中,则去二级缓存获取A单例,也不存在,则去三级缓存获取A单例,存在,则将a对象加入到二级缓存(这个不是lambd表达式,a对象是实例化但还没初始化的a对象)并把三级缓存中的a对象(lambd表达式)去除掉,然后拿a对象注入到b对象属性里的引用A,然后接着b对象的所有属性注入完成,将b对象放入一级缓存,然后将b对象返回。
- 因为以上是个递归调用过程,所以函数返回到第1步的a对象引用B属性依赖注入b对象,这时因为第3三步的返回结果获得b对象,然后执行依赖注入,接着依赖注入a对象的其他属性,完成后将a对象放入一级缓存,然后在二级缓存和三级缓存中移除a对象。
为什么需要三级缓存而不是二级缓存:
如果不考虑AOP,那么二级缓存就足够;如果二级缓存考虑AOP,那么 实例化但未初始化的对象 将存放于二级缓存,其代理对象也将如此,那么可能造成在 对象实例化但未初始化且还没产生代理对象的,某个程序可能拿到了这个对象,然后 这个对象产生了代理对象,另外其他程序就拿到这个代理对象,导致两个程序拿到的是不同对象。
三级缓存在AOP的运用:
第三级缓存存放的是lambd表达式,用来产生所对应的对象,而如果有配置AOP,那么所对应的代理对象就是在这个表达式产生的。在这个表达式中会判断这个对象有没有继承AbstractAutoProxyCreator(AbstractAutoProxyCreator是BeanPostProcessor的子类,AOP就是这样实现的),有则产生代理对象。这样在三级缓存中,每个程序拿到的都是这个lambd表达式,所以产生的都是同样的代理对象。
1.6 FactoryBean与BeanFactory
BeanFactory就是我们平时所说的IOC容器了。
FactoryBean是生产Bean的产品,继承了FactoryBean的对象要实现getObjectType()以及getObject()方法以及isSingleton()方法,getObjectType()这个方法是返回要生产对象的类型,getObject()这个方法返回的对象就是生产的Bean对象,isSingleton()这个方法返回这个要生产的对象是否单例。调用这个getObject()方法是在getBean(Class requiredType)这个方法中,参数requiredType是class类,在getBean中,首先判断IOC容器是否有这个requiredType类型的对象,如果没有,则从继承了FactoryBean的类查找,首先使用getObjectType()判断是否是自己要寻找的类型,若是则调用getObject()生成对象(getObject()因为是自己实现了,所以可以自定义一些操作),然后通过isSingleton()来判断这个对象是否是单例从而执行对应的操作,这样getBean(Class requiredType)方法就能获取到FactoryBean生产的对象。
推荐阅读
-
spring源码深度解析— IOC 之 容器的基本实现
-
基于Spring注解的上下文初始化过程源码解析(一)
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
spring源码分析系列5:ApplicationContext的初始化与Bean生命周期
-
spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库
-
Spring源码剖析2:Spring IOC容器的加载过程
-
Spring源码分析之IoC容器初始化
-
Bootstrap初始化过程源码分析--netty客户端的启动
-
Spring源码分析-IOC容器BeanFactory的应用场景
-
Guzz源码分析(一) guzz容器初始化过程