Spring版本基於:

1、配置啟動Spring所需的監聽器
web.xml中配置監聽器
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3
1
<listener>
2
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
3
</listener>
這是一個典型的 ServletContextListener,Servlet 容器(如 Tomcat 等)在啟動時會找到 ContextLoaderListener 並執行其 contextInitialized(ServletContextEvent event) 方法。
ContextLoaderListener.java extends ContextLoader implements ServletContextListener
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
9
1
ContextLoaderListener.java extends ContextLoader implements ServletContextListener
2
3
public void contextInitialized(ServletContextEvent event) {
4
this.contextLoader = createContextLoader();
5
if (this.contextLoader == null) {
6
this.contextLoader = this;
7
}
8
this.contextLoader.initWebApplicationContext(event.getServletContext());
9
}
從這里開始,Spring 將會進行 Bean Definition的解析、Bean Processors 設置和處理、Beans 實例化等工作。從而將程序會用到的 Java 對象定義並根據該定義創建好,提供給開發人員去使用。
這里 Spring 首先需要處理的就是 Bean 的定義。經過不斷的發展和演化,Bean 的定義方式有:
- 基於 XML 文件的配置方式
- 基於 Annotation 的配置方式
- 基於 Java Code 的配置方式
- 用戶自定義的配置方式
這里就基於 XML 配置 Bean Definition 的源碼進行解讀學習
2、監聽器都做了些什么?
Servlet 容器啟動時如果 web.xml 配置了 ContextLoaderListener,則會調用該對象的初始化方法。根據 Java 語法規定,ContextLoaderListener 的父類 ContextLoader 有一段 static 的代碼會更早被執行。
ContextLoader.java
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
//private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}
15
1
ContextLoader.java
2
3
static {
4
// Load default strategy implementations from properties file.
5
// This is currently strictly internal and not meant to be customized
6
// by application developers.
7
try {
8
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
9
//private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
10
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
11
}
12
catch (IOException ex) {
13
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
14
}
15
}
這里的ContextLoader.properties(spring-web-x.x.x.RELEASE.jar --> org.springframework.web.context.support --> ContextLoader.properties)內容為
ContextLoader.properties
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
3
1
ContextLoader.properties
2
3
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
這段代碼配置了 XML 默認使用的 Context 為 org.springframework.web.context.WebApplicationContext = org.springframework.web.context.support.XmlWebApplicationContext。根據該定義,如果開發人員沒有從 web.xml 指定 contextClass 參數,則默認使用 XmlWebApplicationContext 作為 root WebApplicationContext 工具類。
好了,我們回到剛才的核心方法 initWebApplicationContext() 中去,在這個方法中,有個核心方法
ContextLoader.java
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
5
1
ContextLoader.java
2
3
// Store context in local instance variable, to guarantee that
4
// it is available on ServletContext shutdown.
5
this.context = createWebApplicationContext(servletContext, parent);
我們再繼續往里探索
ContextLoader.java
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
//step1
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
//step2
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// Assign the best possible id value.
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
// Servlet <= 2.4: resort to name specified in web.xml, if any.
String servletContextName = sc.getServletContextName();
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(servletContextName));
}
else {
// Servlet 2.5's getContextPath available!
try {
String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(contextPath));
}
catch (Exception ex) {
throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
}
}
//step3
wac.setParent(parent);
wac.setServletContext(sc);
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
customizeContext(sc, wac);
//step4
wac.refresh();
return wac;
}
43
1
ContextLoader.java
2
3
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
4
//step1
5
Class<?> contextClass = determineContextClass(sc);
6
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
7
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
8
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
9
}
10
11
//step2
12
ConfigurableWebApplicationContext wac =
13
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
14
15
// Assign the best possible id value.
16
if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
17
// Servlet <= 2.4: resort to name specified in web.xml, if any.
18
String servletContextName = sc.getServletContextName();
19
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
20
ObjectUtils.getDisplayString(servletContextName));
21
}
22
else {
23
// Servlet 2.5's getContextPath available!
24
try {
25
String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
26
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
27
ObjectUtils.getDisplayString(contextPath));
28
}
29
catch (Exception ex) {
30
throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
31
}
32
}
33
34
//step3
35
wac.setParent(parent);
36
wac.setServletContext(sc);
37
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
38
customizeContext(sc, wac);
39
40
//step4
41
wac.refresh();
42
return wac;
43
}
3、createWebApplicationContext
3.1 //step1
Class<?> contextClass = determineContextClass(sc);
ContextLoader.java
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); //CONTEXT_CLASS_PARAM = "contextClass"
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else {
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
//defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); defaultStrategies也就是靜態代碼塊中初始化的默認工具類XmlWebApplicationContext
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
25
1
ContextLoader.java
2
3
protected Class<?> determineContextClass(ServletContext servletContext) {
4
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); //CONTEXT_CLASS_PARAM = "contextClass"
5
if (contextClassName != null) {
6
try {
7
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
8
}
9
catch (ClassNotFoundException ex) {
10
throw new ApplicationContextException(
11
"Failed to load custom context class [" + contextClassName + "]", ex);
12
}
13
}
14
else {
15
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
16
//defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); defaultStrategies也就是靜態代碼塊中初始化的默認工具類XmlWebApplicationContext
17
try {
18
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
19
}
20
catch (ClassNotFoundException ex) {
21
throw new ApplicationContextException(
22
"Failed to load default context class [" + contextClassName + "]", ex);
23
}
24
}
25
}
首先determineContextClass()方法查明具體的Context類,他會讀取servletContext的初始化參數contextClass,此參數我們一般不配置。
所以Spring就會讀取跟org.springframework.web.context.WebApplicationContext同一個包下面的ContextLoader.properties文件讀取默認設置,反射出org.springframework.web.context.support.XmlWebApplicationContext類來。
以圖友網項目為例(添加鏈接),此時返回的為 return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());

3.2 //step2
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
接下來就是通過BeanUtils的方法把新創建的XmlWebApplicationContext進行初始化。

注意看類別的變化
3.3 //step3
對於得到的這個 (
ConfigurableWebApplicationContext) XmlWebApplicationContext
然后對之中的屬性進行一系列的設置,
首先會設置一個默認ID,即org.springframework.web.context.WebApplicationContext:+你項目的ContextPath。
然后再設置其他屬性
wac.setParent(parent);
wac.setServletContext(sc);
其中還需要設置
wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
//CONFIG_LOCATION_PARAM = "contextConfigLocation"
//這里設置了你在web.xml中對於applicationContext.xml的地址配置
//e.g.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
4
1
<context-param>
2
<param-name>contextConfigLocation</param-name>
3
<param-value>classpath:applicationContext.xml</param-value>
4
</context-param>
也就是說,你在配置文件中的那些beans,這時候容器已經知道要准備哪些bean了,雖然還沒有進行實例化,就像一個工廠已經得到了產品的加工圖紙,但是還沒有去加工實際產品出來。

customizeContext(sc, wac);
接下來就是customizeContext(sc, wac)方法,此方法會根據用戶配置的globalInitializerClasses參數來初始化一些用戶自定義的屬性,一般我們不配置,所以這里什么也不做。
3.4 //step4
wac.refresh();
最后登場的就是最核心的方法了,在這個方法里,會完成資源文件的加載、配置文件解析、Bean定義的注冊、組件的初始化等核心工作。
AbstractApplicationContext.java
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//此方法做一些准備工作,如記錄開始時間,輸出日志,initPropertySources();和getEnvironment().validateRequiredProperties();一般沒干什么事
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//step4.1
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.
//step4.2
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
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;
}
}
}
60
1
AbstractApplicationContext.java
2
3
4
public void refresh() throws BeansException, IllegalStateException {
5
synchronized (this.startupShutdownMonitor) {
6
// Prepare this context for refreshing.
7
//此方法做一些准備工作,如記錄開始時間,輸出日志,initPropertySources();和getEnvironment().validateRequiredProperties();一般沒干什么事
8
prepareRefresh();
9
10
// Tell the subclass to refresh the internal bean factory.
11
//step4.1
12
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
13
14
// Prepare the bean factory for use in this context.
15
prepareBeanFactory(beanFactory);
16
17
try {
18
// Allows post-processing of the bean factory in context subclasses.
19
postProcessBeanFactory(beanFactory);
20
21
// Invoke factory processors registered as beans in the context.
22
invokeBeanFactoryPostProcessors(beanFactory);
23
24
// Register bean processors that intercept bean creation.
25
registerBeanPostProcessors(beanFactory);
26
27
// Initialize message source for this context.
28
initMessageSource();
29
30
// Initialize event multicaster for this context.
31
initApplicationEventMulticaster();
32
33
// Initialize other special beans in specific context subclasses.
34
onRefresh();
35
36
// Check for listener beans and register them.
37
registerListeners();
38
39
// Instantiate all remaining (non-lazy-init) singletons.
40
//step4.2
41
finishBeanFactoryInitialization(beanFactory);
42
43
// Last step: publish corresponding event.
44
finishRefresh();
45
}
46
47
catch (BeansException ex) {
48
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
49
50
// Destroy already created singletons to avoid dangling resources.
51
destroyBeans();
52
53
// Reset 'active' flag.
54
cancelRefresh(ex);
55
56
// Propagate exception to caller.
57
throw ex;
58
}
59
}
60
}
3.4.1 //step4.1 refresh()的核心 obtainFreshBeanFactory()
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
初始化BeanFactory,是整個refresh()方法的核心,其中完成了配置文件的加載、解析、注冊。
AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
10
1
AbstractApplicationContext.java
2
3
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
4
refreshBeanFactory();
5
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
6
if (logger.isDebugEnabled()) {
7
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
8
}
9
return beanFactory;
10
}
跟進一下refreshBeanFactory();
AbstractRefreshableApplicationContext.java
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
20
1
AbstractRefreshableApplicationContext.java
2
3
protected final void refreshBeanFactory() throws BeansException {
4
if (hasBeanFactory()) {
5
destroyBeans();
6
closeBeanFactory();
7
}
8
try {
9
DefaultListableBeanFactory beanFactory = createBeanFactory();
10
beanFactory.setSerializationId(getId());
11
customizeBeanFactory(beanFactory);
12
loadBeanDefinitions(beanFactory);
13
synchronized (this.beanFactoryMonitor) {
14
this.beanFactory = beanFactory;
15
}
16
}
17
catch (IOException ex) {
18
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
19
}
20
}
DefaultListableBeanFactory beanFactory = createBeanFactory();
在這個beanFactory中有個beanDefinitionMap,此時size=0
再跟進loadBeanDefinitions(beanFactory);
AbstractXmlApplicationContext.java
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.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.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
17
1
AbstractXmlApplicationContext.java
2
3
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
4
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
5
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
6
7
// Configure the bean definition reader with this context's
8
// resource loading environment.
9
beanDefinitionReader.setEnvironment(this.getEnvironment());
10
beanDefinitionReader.setResourceLoader(this);
11
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
12
13
// Allow a subclass to provide custom initialization of the reader,
14
// then proceed with actually loading the bean definitions.
15
initBeanDefinitionReader(beanDefinitionReader);
16
loadBeanDefinitions(beanDefinitionReader);
17
}
我們跟進這個方法
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
8
1
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
2
String[] configLocations = getConfigLocations();
3
if (configLocations != null) {
4
for (String configLocation : configLocations) {
5
reader.loadBeanDefinitions(configLocation);
6
}
7
}
8
}
在3.3中我們提到,configLocations已經被得到,那么此時可以看到:

這里設計了層層調用,有很多重載方法,主要就是加載Spring所有的配置文件(可能會有多個),以備后面解析,注冊之用。
然后追蹤到 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(Element root) 中
DefaultBeanDefinitionDocumentReader.java
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(this.readerContext, root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
preProcessXml(root);
//xml解析和加載類
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
32
1
DefaultBeanDefinitionDocumentReader.java
2
3
protected void doRegisterBeanDefinitions(Element root) {
4
// Any nested <beans> elements will cause recursion in this method. In
5
// order to propagate and preserve <beans> default-* attributes correctly,
6
// keep track of the current (parent) delegate, which may be null. Create
7
// the new (child) delegate with a reference to the parent for fallback purposes,
8
// then ultimately reset this.delegate back to its original (parent) reference.
9
// this behavior emulates a stack of delegates without actually necessitating one.
10
BeanDefinitionParserDelegate parent = this.delegate;
11
this.delegate = createDelegate(this.readerContext, root, parent);
12
13
if (this.delegate.isDefaultNamespace(root)) {
14
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
15
if (StringUtils.hasText(profileSpec)) {
16
Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
17
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
18
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
19
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
20
return;
21
}
22
}
23
}
24
25
preProcessXml(root);
26
27
//xml解析和加載類
28
parseBeanDefinitions(root, this.delegate);
29
postProcessXml(root);
30
31
this.delegate = parent;
32
}
BeanDefinitionParserDelegate parent = this.delegate;
這里創建了一個BeanDefinitionParserDelegate實例,解析XML的過程就是委托它完成的。實際上你跟進該類,可以發現里面定義了大量的常量,這些常量實際上就是我們在xml中使用到的節點和屬性名。
e.g.
...
public static final String PROPERTY_ELEMENT = "property";
public static final String REF_ATTRIBUTE = "ref";
public static final String VALUE_ATTRIBUTE = "value";
...
9
1
...
2
3
public static final String PROPERTY_ELEMENT = "property";
4
5
public static final String REF_ATTRIBUTE = "ref";
6
7
public static final String VALUE_ATTRIBUTE = "value";
8
9
...


這里的delegate實際上也是 BeanDefinitionParserDelegate 類,所以我們看到,在 parseBeanDefinitions(root, this.delegate); 中,是將該類作為一個參數引入了方法,實際上,它在方法中就發揮着解析xml的作用。
//xml解析和加載類
parseBeanDefinitions(root, this.delegate);
DefaultBeanDefinitionDocumentReader.java
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes(); //將節點獲取存入collection
for (int i = 0; i < nl.getLength(); i++) { //對collection中存儲的節點進行依次遍歷
Node node = nl.item(i); //返回當前序號的節點
if (node instanceof Element) { //判斷節點是否屬於元素類(我們不需要文本型)(參考文章《Java是如何解析xml文件的(DOM)》)
Element ele = (Element) node;
//判斷是否為默認的命名空間,其實就是根據配置文件的命名空間來判定
//如果是beans下的則認為是默認的命名空間,如果不是則認為是自定義的,我們使用的Aop、Tx等都是屬於自定義標簽的范疇
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate); //進行解析
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
24
1
DefaultBeanDefinitionDocumentReader.java
2
3
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
4
if (delegate.isDefaultNamespace(root)) {
5
NodeList nl = root.getChildNodes(); //將節點獲取存入collection
6
for (int i = 0; i < nl.getLength(); i++) { //對collection中存儲的節點進行依次遍歷
7
Node node = nl.item(i); //返回當前序號的節點
8
if (node instanceof Element) { //判斷節點是否屬於元素類(我們不需要文本型)(參考文章《Java是如何解析xml文件的(DOM)》)
9
Element ele = (Element) node;
10
//判斷是否為默認的命名空間,其實就是根據配置文件的命名空間來判定
11
//如果是beans下的則認為是默認的命名空間,如果不是則認為是自定義的,我們使用的Aop、Tx等都是屬於自定義標簽的范疇
12
if (delegate.isDefaultNamespace(ele)) {
13
parseDefaultElement(ele, delegate); //進行解析
14
}
15
else {
16
delegate.parseCustomElement(ele);
17
}
18
}
19
}
20
}
21
else {
22
delegate.parseCustomElement(root);
23
}
24
}
最終,我們可以看到解析XML的是 parseDefaultElement(ele, delegate); 方法,它會判斷並調用對應的解析,這里我們是bean
DefaultBeanDefinitionDocumentReader.java
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
17
1
DefaultBeanDefinitionDocumentReader.java
2
3
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
4
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
5
importBeanDefinitionResource(ele);
6
}
7
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
8
processAliasRegistration(ele);
9
}
10
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
11
processBeanDefinition(ele, delegate);
12
}
13
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
14
// recurse
15
doRegisterBeanDefinitions(ele);
16
}
17
}
然后進一步
DefaultBeanDefinitionDocumentReader.java
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
18
1
DefaultBeanDefinitionDocumentReader.java
2
3
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
4
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
5
if (bdHolder != null) {
6
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
7
try {
8
// Register the final decorated instance.
9
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
10
}
11
catch (BeanDefinitionStoreException ex) {
12
getReaderContext().error("Failed to register bean definition with name '" +
13
bdHolder.getBeanName() + "'", ele, ex);
14
}
15
// Send registration event.
16
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
17
}
18
}
這個方法如果進一步深入,你可以發現它實際上最核心的兩個步驟是:
- 把beanName放到隊列里
- 把BeanDefinition放到map中
(關於這段注冊bean的方法的源碼跟進,可以參考博文:《
Spring Ioc 源碼分析(四)--parseBeanDefinitions()與BeanDefinitionParserDelegate》)

好了,執行完 parseBeanDefinitions 這個方法,我們看看現在的 delegate 里面多了些什么?


到此,bean的注冊就完成了(當然,這里是指所有的bean都注冊完)。在后面實例化的時候,就是把beanDefinitionMap中的BeanDefinition取出來,逐一實例化。
obtainFreshBeanFactory() 總算結束了,我們繼續看refresh()方法中另一個核心方法,它是將bean進行實例化的重要角色。
3.4.2 //step4.2 refresh()的核心finishBeanFactoryInitialization(beanFactory);
我想到這里估計已經暈乎了,如果不太清楚我們現在走到了哪里,請查看目錄中的 3.4 //step4
經過obtainFreshBeanFactory() 這個方法,我們的beanFactory就准備好了,接下來
我們主要圍繞finishBeanFactoryInitialization(beanFactory)方法,聊聊Spring是如何實例化bean的。
AbstractApplicationContext.java
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
25
1
AbstractApplicationContext.java
2
3
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
4
// Initialize conversion service for this context.
5
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
6
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
7
beanFactory.setConversionService(
8
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
9
}
10
11
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
12
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
13
for (String weaverAwareName : weaverAwareNames) {
14
getBean(weaverAwareName);
15
}
16
17
// Stop using the temporary ClassLoader for type matching.
18
beanFactory.setTempClassLoader(null);
19
20
// Allow for caching all bean definition metadata, not expecting further changes.
21
beanFactory.freezeConfiguration();
22
23
// Instantiate all remaining (non-lazy-init) singletons.
24
beanFactory.preInstantiateSingletons();
25
}
這個方法,就是為了實例化非懶加載的
單例bean
,我們走進 beanFactory.preInstantiateSingletons(); 看一看
(注意,這里實例化單例,而Struts中Action是每次請求都創建,所以Action並不是單例的)
DefaultListableBeanFactory.java
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}
List<String> beanNames;
synchronized (this.beanDefinitionMap) {
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
beanNames = new ArrayList<String>(this.beanDefinitionNames);
}
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) { //將加載進來的beanDefinitionNames循環分析
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果不是抽象類, 且是單例, 且不是延遲加載
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
x
1
DefaultListableBeanFactory.java
2
3
public void preInstantiateSingletons() throws BeansException {
4
if (this.logger.isDebugEnabled()) {
5
this.logger.debug("Pre-instantiating singletons in " + this);
6
}
7
8
List<String> beanNames;
9
synchronized (this.beanDefinitionMap) {
10
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
11
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
12
beanNames = new ArrayList<String>(this.beanDefinitionNames);
13
}
14
15
// Trigger initialization of all non-lazy singleton beans...
16
for (String beanName : beanNames) { //將加載進來的beanDefinitionNames循環分析
17
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
18
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果不是抽象類, 且是單例, 且不是延遲加載
19
if (isFactoryBean(beanName)) {
20
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
21
boolean isEagerInit;
22
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
23
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
24
25
public Boolean run() {
26
return ((SmartFactoryBean<?>) factory).isEagerInit();
27
}
28
}, getAccessControlContext());
29
}
30
else {
31
isEagerInit = (factory instanceof SmartFactoryBean &&
32
((SmartFactoryBean<?>) factory).isEagerInit());
33
}
34
if (isEagerInit) {
35
getBean(beanName);
36
}
37
}
38
else {
39
getBean(beanName);
40
}
41
}
42
}
43
44
// Trigger post-initialization callback for all applicable beans...
45
for (String beanName : beanNames) {
46
Object singletonInstance = getSingleton(beanName);
47
if (singletonInstance instanceof SmartInitializingSingleton) {
48
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
49
if (System.getSecurityManager() != null) {
50
AccessController.doPrivileged(new PrivilegedAction<Object>() {
51
52
public Object run() {
53
smartSingleton.afterSingletonsInstantiated();
54
return null;
55
}
56
}, getAccessControlContext());
57
}
58
else {
59
smartSingleton.afterSingletonsInstantiated();
60
}
61
}
62
}
63
}
因為Struts項目中Action並不滿足條件 “不是抽象類, 且是單例, 且不是延遲加載”,所以該方法對我們自定義的Action幾乎沒有用,我們一直循環直到單例的對象出現,再來看這個代碼。
我們把這小段代碼提出來單獨看
for (String beanName : beanNames) { //將加載進來的beanDefinitionNames循環分析
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果不是抽象類, 且是單例, 且不是延遲加載
if (isFactoryBean(beanName)) { //是否實現FactoryBean接口
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
1
for (String beanName : beanNames) { //將加載進來的beanDefinitionNames循環分析
2
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
3
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //如果不是抽象類, 且是單例, 且不是延遲加載
4
if (isFactoryBean(beanName)) { //是否實現FactoryBean接口
5
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
6
boolean isEagerInit;
7
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
8
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
9
10
public Boolean run() {
11
return ((SmartFactoryBean<?>) factory).isEagerInit();
12
}
13
}, getAccessControlContext());
14
}
15
else {
16
isEagerInit = (factory instanceof SmartFactoryBean &&
17
((SmartFactoryBean<?>) factory).isEagerInit());
18
}
19
if (isEagerInit) {
20
getBean(beanName);
21
}
22
}
23
else {
24
getBean(beanName);
25
}
26
}
27
}
- 判斷這個bean是否是抽象類,是否是單例,是否延遲加載
- 如果不是抽象類, 且是單例, 且不是延遲加載,那么判斷是否實現 FactoryBean 接口
- 如果實現了 FactoryBean,則 getBean(FACTORY_BEAN_PREFIX + beanName),否則 getBean(beanName)
(參考鏈接:)
如果我們跟進 getBean 這個方法,發現它調用了 doGetBean 這個方法,我們再跟進,這個方法非常長(這里就不貼出來了)
在這個方法中,你可以不斷地去跟進(這里不再做具體展開),你會發現大概的步驟差不多是
- 創建一個bean的實例
- 將這個實例封裝到BeanWrapper中

而這里bean的實例化方法,其實是 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
這個instantiate 方法在 package org.springframework.beans.factory.support; --> SimpleInstantiationStrategy.java
在這之中采用反射機制將對象進行了實例化。
其實還涉及到bean實例化以后,Spring是如何將bean的屬性進行注入的,這里暫時不做進一步的展開了。
可以知道的是,最終屬性的注入是利用反射機制,通過setter賦值的。