上一篇说到的ioc容器的refresh()方法,是整个容器启动的最核心方法,在这个refresh()方法中,会完成资源文件的加载、配置文件解析、Bean定义的注册、组件的初始化等核心工作。
让我们来look look 这个refresh()方法到底干了啥事情~~~
惯例先贴源码,先看下refresh()方法的说明。
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()
主要就是一些准备操作,记录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的实现
第一步refreshBeanFactory
顾名思义,刷新beanFactory,在刷新的时候,判断是否已经有beanFactory了,如果有相关beanFactory,则destory所有单利的bean,并且关闭beanFactory,在关闭了原来旧的beanFactory后,再创建一个新的beanFactory,创建了新的beanFactory后,完成加载bean的配置文件,解析bean等一系列相关操作。
先进入hasBeanFactory()
又是一段同步代码块,有没有很熟悉,又是同步了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()方法中
先通过InputStream读取当前已经被加载的配置文件,读取好后再通过
doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法进行加载,进入doLoadBeanDefinitions方法
可以看出,在doLoadBeanDefinitions方法中,try代码块中第一行方法主要做的就是将配置文件加载成
Document类型,具体怎么处理的这里先不细说。真正在执行bean加载的,就是第二行的方法。
进入registerBeanDefinitions(doc, resource)方法
显而易见的,方法中的第三行代码,进行了bean的加载和注册
继续进入doRegisterBeanDefinitions(root)
方法里面先是创建了一个BeanDefinitionParserDelegate,具体怎么解析xml的,这里先跳过,我们的目标是知道怎么加载bean的。
看parseBeanDefinitions(root, this.delegate)这个方法
方法上的注释说的很明白:解析document中root level(import,alias,bean标签)的element。
先判断是否为defaultNameSpace,通过判断element的父类Node中的NamespaceURI,是否与创建的
BeanDefinitionParserDelegate中的属性BEANS_NAMESPACE_URI中的值是否相等。
先贴上配置文件和路径,结合配置文件说会比较清楚。
resources下面两个目录,一个config,一个spring
在启动的时候,debug到上面的parseBeanDefinitions方法中,我们先看看下delegate中读取的是哪个配置文件:
很明显,先读取了spring文件夹下面的servlet-context.xml配置文件。
再看servlet-context.xml配置文件中的内容。
首先配置的是配置文件注入的bean,注意上面的注释,在解析的时候也会被读取,但是由于不是Element的实例,所以不会进行处理。
再下一步继续debug
进入parseDefaultElement(ele,delegate)方法,
判断delegate中node的name,是否为root level的name标签(import,alias,beans,bean)
由于是ele是bean标签,所以会进入第二个elseif delegate.nodeNameEquals(ele, BEAN_ELEMENT)判断
再走下一步看:
方法中的第一行,就是把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);
}
}
看最后几行代码,
往beanDefinitionMap这个map中放值,key是beanName,value是beanDefinition
往beanDefinitionNames这个arrayList中add beanName,看一下这个beanDefinitionNames的注释:
根据注册顺序,往list中add name。
add完bean的name和definition后,就返回了,回到parseBeanDefinitions继续解析下一个doc。
继续看配置文件:
再解析完bean标签后,跳过注释,下一个标签是
进入parseCustomElement(ele)中,
首先根据当前element元素的namespaceURI,获得对应的handler,其实就是根据不同的xml标签,找出合适的BeanDefinitionParser。
由于我们在xml配置文件中配置了
其实是 org.springframework.context.annotation.ComponentScanBeanDefinitionParser
进入ComponentScanBeanDefinitionParser的parse()方法
basePackage的值其实就是xml中base-package后面我们填写的包名。
再往下,创建了一个ClassPathBeanDefinitionScanner,注释说的很清楚,扫描bean的定义然后注册他们,注意:这里是类文件而不是类,因为现在这些类还没有被加载,只是ClassLoader能找到这些class的路径而已
感觉离真相越来越近了。。。继续
进入scanner.doScan()看看,是怎么扫描的
如果basePackages为空,提示至少需要指定一个base-package
进入for循环,
Set candidates = findCandidateComponents(basePackage);
这一句,意思是根据basePackage找到所有的包中的类,当做候选人。
再进去look look里面干了什么
又是一大坨代码,是不是有点晕啊
一句一句来分析,先看这一句
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的版本中可以看到直接声明:
Spring4.3.6中移除了后面的new实例化。resolver中本质还是通过AntPathMatcher进行匹配。
进入getResources(packageSearchPath);
首先会判断locationPattern是否以"classpatch:*"开头,上面spring已经替我们加了这个前缀, 所以这里符合条件
下一步进入findPathMatchingResources(),
假设我们的locationPattern是:classpath*:com/abc/def/**/*.class
determineRootDir(locationPattern)方法做的就是获得根文件夹目录,去掉/**/*.class
则rootDirPath=classpath*:com/abc/def/
subPattern=**/*.class
继续下一步,回到getResources(rootDirPath)方法,此时依旧符合以classpath*开头,但是不满足if判断条件,进入else逻辑:
继续下一步:
zzz,进入find方法后,location="com/abc/def",
下面的ClassLoader cl = getClassLoader()
说明了Spring也是通过用的ClassLoader
加载的class文件。
到此为止,就获得了bean的class文件了,最终spring会将class封装成beanDefinition。
回到doScan中,
最终,又回到了registerBeanDefinition(definitionHolder, this.registry);就是本文上面说把beanName放入DefaultListableBeanFactory中的beanDefinitionMap里面。
到这一步,spring已经获得了bean的class文件了,具体spring是怎么实例化bean的,下一篇继续说。
水平有限,有不对的地方请指正,谢谢