Spring启动过程(二)

    上一篇说到的ioc容器的refresh()方法,是整个容器启动的最核心方法,在这个refresh()方法中,会完成资源文件的加载、配置文件解析、Bean定义的注册、组件的初始化等核心工作。

    让我们来look look 这个refresh()方法到底干了啥事情~~~

 

惯例先贴源码,先看下refresh()方法的说明。

Spring启动过程(二)_第1张图片

refresh()方法是ConfigurableApplicationContext接口提供的方法,refresh()方法的具体实现是由

AbstractApplicationContext这个抽象类实现的。

AbstractApplicationContext这个抽象类继承了DefaultResourceLoader,并且实现了接口

ConfigurableApplicationContext

废话不多,上源码?

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();
		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);
		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);
			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);
			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);
			// Initialize message source for this context.
			initMessageSource();
			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();
			// Initialize other special beans in specific context subclasses.
			onRefresh();
			// Check for listener beans and register them.
			registerListeners();
			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);
			// Last step: publish corresponding event.
			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();
		}
	}
}

显而易见的,refresh()中是一段同步代码块,this.startupShutdownMonitor对象是一个Object对象,主要作用其实就是 用于“刷新”和“销毁”的同步监视器,换句话说,就是在refresh()和close()两个方法中,都会同步此对象,用来保证统时间只有一个线程执行刷新或者关闭操作。

1. prepareRefresh()

Spring启动过程(二)_第2张图片

主要就是一些准备操作,记录context的startup时间,设置closed为false,active为true,其中,设置active为true这一步,对应在ContextLoader中的initWebApplicationContext方法,用于判断当前context对象是否被刷新过。

回到refresh方法中,接下来是obtainFreshBeanFactory()方法。

2.obtainFreshBeanFactory()

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

obtainFreshBeanFactory()方法是整个refresh()方法的核心,主要做完成BeanFactory的创建,bean配置文件的加载,解析和注册。

来看obtainFreshBeanFactory的实现

Spring启动过程(二)_第3张图片

第一步refreshBeanFactory

Spring启动过程(二)_第4张图片

顾名思义,刷新beanFactory,在刷新的时候,判断是否已经有beanFactory了,如果有相关beanFactory,则destory所有单利的bean,并且关闭beanFactory,在关闭了原来旧的beanFactory后,再创建一个新的beanFactory,创建了新的beanFactory后,完成加载bean的配置文件,解析bean等一系列相关操作。

先进入hasBeanFactory()

Spring启动过程(二)_第5张图片

又是一段同步代码块,有没有很熟悉,又是同步了this.beanFactoryMonitor,用于判断当前是否有已经持有了一个bean factory,并且至少已经被刷新了一次,未被关闭的。

如果返回有bean factory,则需要destory所有单例的bean,因为bean factory只能管理单例的bean,非单例的bean不归bean factory管,所以只destory了所有单利的bean,并且关闭了bean factory。

在关闭了bean factory后,再次创建的factory是 DefaultListableBeanFactory,创建好factory后,设置序列号,个性化设置。

重点来了,

loadBeanDefinitions(factory);

这个方法,主要做了加载bean到factory中

通过XmlBeanDefinitionReader进行加载bean的定义,进入loadBeanDefinitions方法,里面调用的层数很多,大部分都是@Override的方法,主要就是用来加载Spring的配置文件,大部分情况下是多个,加载好配置文件后用于后面的解析。

最后真正执行加载配置文件xml的地方,是在XmlBeanDefinitionReader里的loadBeanDefinitions()方法中

Spring启动过程(二)_第6张图片

先通过InputStream读取当前已经被加载的配置文件,读取好后再通过

doLoadBeanDefinitions(InputSource inputSource, Resource resource)

方法进行加载,进入doLoadBeanDefinitions方法

Spring启动过程(二)_第7张图片

可以看出,在doLoadBeanDefinitions方法中,try代码块中第一行方法主要做的就是将配置文件加载成

Document类型,具体怎么处理的这里先不细说。真正在执行bean加载的,就是第二行的方法。

进入registerBeanDefinitions(doc, resource)方法

Spring启动过程(二)_第8张图片

显而易见的,方法中的第三行代码,进行了bean的加载和注册

Spring启动过程(二)_第9张图片

继续进入doRegisterBeanDefinitions(root)

Spring启动过程(二)_第10张图片

方法里面先是创建了一个BeanDefinitionParserDelegate,具体怎么解析xml的,这里先跳过,我们的目标是知道怎么加载bean的。

看parseBeanDefinitions(root, this.delegate)这个方法

Spring启动过程(二)_第11张图片

方法上的注释说的很明白:解析document中root level(import,alias,bean标签)的element。

先判断是否为defaultNameSpace,通过判断element的父类Node中的NamespaceURI,是否与创建的

BeanDefinitionParserDelegate中的属性BEANS_NAMESPACE_URI中的值是否相等。

先贴上配置文件和路径,结合配置文件说会比较清楚。

Spring启动过程(二)_第12张图片

resources下面两个目录,一个config,一个spring

在启动的时候,debug到上面的parseBeanDefinitions方法中,我们先看看下delegate中读取的是哪个配置文件:

Spring启动过程(二)_第13张图片

很明显,先读取了spring文件夹下面的servlet-context.xml配置文件。

再看servlet-context.xml配置文件中的内容。

Spring启动过程(二)_第14张图片

首先配置的是配置文件注入的bean,注意上面的注释,在解析的时候也会被读取,但是由于不是Element的实例,所以不会进行处理。

再下一步继续debug

Spring启动过程(二)_第15张图片

进入parseDefaultElement(ele,delegate)方法,

Spring启动过程(二)_第16张图片

判断delegate中node的name,是否为root level的name标签(import,alias,beans,bean)

由于是ele是bean标签,所以会进入第二个elseif delegate.nodeNameEquals(ele, BEAN_ELEMENT)判断

Spring启动过程(二)_第17张图片

再走下一步看:

Spring启动过程(二)_第18张图片

方法中的第一行,就是把ele标签中的配置,解析成一个BeanDefinitionHolder,

 

解析完成后,我们可以看到,整个BeanDefinitionHolder对象中,属性beanName已经有值了,对应的就是上面servlet-context.xml中bean标签的id里面的值,beanDefinition中就是bean的所有定义,包括bean的class,是否singleton,是否lazy-init等属性配置。

再往下走,进入try中的registerBeanDefinition这个方法,主要就是注册当前bean到当前的beanfactory中。

 

继续深入registerBeanDefinition(String beanName, BeanDefinition beanDefinition),代码较长==

@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);
		}
	}
	BeanDefinition oldBeanDefinition;
	oldBeanDefinition = this.beanDefinitionMap.get(beanName);
	if (oldBeanDefinition != null) {
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
					"': There is already [" + oldBeanDefinition + "] bound.");
		}
		else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
			if (this.logger.isWarnEnabled()) {
				this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
						"' with a framework-generated bean definition: replacing [" +
						oldBeanDefinition + "] with [" + beanDefinition + "]");
			}
		}
		else if (!beanDefinition.equals(oldBeanDefinition)) {
			if (this.logger.isInfoEnabled()) {
				this.logger.info("Overriding bean definition for bean '" + beanName +
						"' with a different definition: replacing [" + oldBeanDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		else {
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Overriding bean definition for bean '" + beanName +
						"' with an equivalent definition: replacing [" + oldBeanDefinition +
						"] 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 updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				updatedDefinitions.add(beanName);
				this.beanDefinitionNames = updatedDefinitions;
				if (this.manualSingletonNames.contains(beanName)) {
					Set updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
					updatedSingletons.remove(beanName);
					this.manualSingletonNames = updatedSingletons;
				}
			}
		}
		else {
			// Still in startup registration phase
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			this.manualSingletonNames.remove(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}
	if (oldBeanDefinition != null || containsSingleton(beanName)) {
		resetBeanDefinition(beanName);
	}
}

看最后几行代码,

Spring启动过程(二)_第19张图片

往beanDefinitionMap这个map中放值,key是beanName,value是beanDefinition

往beanDefinitionNames这个arrayList中add beanName,看一下这个beanDefinitionNames的注释:

225641_i7AF_2248830.png

根据注册顺序,往list中add name。

add完bean的name和definition后,就返回了,回到parseBeanDefinitions继续解析下一个doc。

继续看配置文件:

225725_n63R_2248830.png

再解析完bean标签后,跳过注释,下一个标签是这个指定扫描包的标签,此标签不是defaultNamespace,所以会跳转到else中的parseCustomElement(ele)方法中。

Spring启动过程(二)_第20张图片

进入parseCustomElement(ele)中,

Spring启动过程(二)_第21张图片

首先根据当前element元素的namespaceURI,获得对应的handler,其实就是根据不同的xml标签,找出合适的BeanDefinitionParser。

 

由于我们在xml配置文件中配置了,可以看到,component-scan对应的handler

其实是 org.springframework.context.annotation.ComponentScanBeanDefinitionParser

进入ComponentScanBeanDefinitionParser的parse()方法

Spring启动过程(二)_第22张图片

basePackage的值其实就是xml中base-package后面我们填写的包名。

再往下,创建了一个ClassPathBeanDefinitionScanner,注释说的很清楚,扫描bean的定义然后注册他们,注意:这里是类文件而不是类,因为现在这些类还没有被加载,只是ClassLoader能找到这些class的路径而已

感觉离真相越来越近了。。。继续

进入scanner.doScan()看看,是怎么扫描的

Spring启动过程(二)_第23张图片

如果basePackages为空,提示至少需要指定一个base-package

进入for循环,

Set candidates = findCandidateComponents(basePackage);

这一句,意思是根据basePackage找到所有的包中的类,当做候选人。

再进去look look里面干了什么

Spring启动过程(二)_第24张图片

又是一大坨代码,是不是有点晕啊

一句一句来分析,先看这一句

String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
      resolveBasePackage(basePackage) + '/' + this.resourcePattern;

好像不太明白啥意思,首先假设我们在xml中的base-package写的是"com.abc.efg",那么,

packageSearchPath=classpath*:com.abc.efg/**/*.class,意思就是com.abc.efg包下面的所有class,包括子包中的class。如果base-package写的是"*",那就是classpath*:*/**/*.class,就是全部的class了。

获得到packageSearchPath后,再去获得Resources

Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);

resourcePatternResolver是一个interface,里面的getResources(String locationPattern)方法,这里使用的实现类是PathMatchingResourcePatternResolver,Spring3的版本中可以看到直接声明:

215505_Rory_2248830.png

Spring4.3.6中移除了后面的new实例化。resolver中本质还是通过AntPathMatcher进行匹配。

进入getResources(packageSearchPath);

Spring启动过程(二)_第25张图片

首先会判断locationPattern是否以"classpatch:*"开头,上面spring已经替我们加了这个前缀, 所以这里符合条件

Spring启动过程(二)_第26张图片

下一步进入findPathMatchingResources(),

Spring启动过程(二)_第27张图片

假设我们的locationPattern是:classpath*:com/abc/def/**/*.class

determineRootDir(locationPattern)方法做的就是获得根文件夹目录,去掉/**/*.class

则rootDirPath=classpath*:com/abc/def/

subPattern=**/*.class

继续下一步,回到getResources(rootDirPath)方法,此时依旧符合以classpath*开头,但是不满足if判断条件,进入else逻辑:

Spring启动过程(二)_第28张图片

继续下一步:

Spring启动过程(二)_第29张图片

zzz,进入find方法后,location="com/abc/def",

下面的ClassLoader cl = getClassLoader()

说明了Spring也是通过用的ClassLoader加载的class文件。

到此为止,就获得了bean的class文件了,最终spring会将class封装成beanDefinition。

回到doScan中,

Spring启动过程(二)_第30张图片

最终,又回到了registerBeanDefinition(definitionHolder, this.registry);就是本文上面说把beanName放入DefaultListableBeanFactory中的beanDefinitionMap里面。

到这一步,spring已经获得了bean的class文件了,具体spring是怎么实例化bean的,下一篇继续说。

 

 

水平有限,有不对的地方请指正,谢谢

 

你可能感兴趣的:(Spring)