1.背景
最近在使用Spring MVC過程中遇到了一些問題,網(wǎng)上搜索不少帖子后雖然找到了答案和解決方法,但這些答案大部分都只是給了結論,并沒有說明具體原因,感覺總是有點不太滿意。
更重要的是這些所謂的結論大多是抄來抄去,基本源自一家,真實性也有待考證。
要成為一名優(yōu)秀的碼農(nóng),不僅能熟練的復制粘貼,更要有打破砂鍋問到底的精神,達到知其然也知其所以然的境界。
那作為程序員怎么能知其所以然呢?
答案就是閱讀源代碼!
此處請大家內(nèi)心默讀三遍。
用過Spring 的人都知道其核心就是IOC和AOP,因此要想了解Spring機制就得先從這兩點入手,本文主要通過對IOC部分的機制進行介紹。
2. 實驗環(huán)境
在開始閱讀之前,先準備好以下實驗材料。
Spring 5.0源碼(https://github.com/spring-projects/spring-framework.git)
IDE:Intellij IDEA
IDEA 是一個優(yōu)秀的開發(fā)工具,如果還在用Eclipse的建議切換到此工具進行。
IDEA有很多的快捷鍵,在分析過程中建議大家多用Ctrl+Alt+B快捷鍵,可以快速定位到實現(xiàn)函數(shù)。
3. Spring Bean 解析注冊
Spring bean的加載主要分為以下6步:
- (1)讀取XML配置文件
- (2)XML文件解析為document文檔
- (3)解析bean
- (4)注冊bean
- (5)實例化bean
- (6)獲取bean
3.1 讀取XML配置文件
查看源碼第一步是找到程序入口,再以入口為突破口,一步步進行源碼跟蹤。
Java Web應用中的入口就是web.xml。
在web.xml找到ContextLoaderListener ,此Listener負責初始化Spring IOC。
contextConfigLocation參數(shù)設置了bean定義文件地址。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring.xml</param-value>
</context-param>
下面是ContextLoaderListener的官方定義:
public class ContextLoaderListener
extends ContextLoader
implements ServletContextListener
Bootstrap listener to start up and shut down Spring's root WebApplicationContext. Simply delegates to ContextLoader as well as to ContextCleanupListener.
翻譯過來ContextLoaderListener作用就是負責啟動和關閉Spring root WebApplicationContext。
具體WebApplicationContext是什么?開始看源碼。
package org.springframework.web.context;
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
//servletContext初始化時候調(diào)用
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext();
}
//servletContext銷毀時候調(diào)用
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
}
}
從源碼看出此Listener主要有兩個函數(shù),一個負責初始化WebApplicationContext,一個負責銷毀。
繼續(xù)看initWebApplicationContext函數(shù)。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//初始化Spring容器時如果發(fā)現(xiàn)servlet 容器中已存在根Spring容根器則拋出異常,證明rootWebApplicationContext只能有一個。
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
if (this.context == null) {
//1.創(chuàng)建webApplicationContext實例
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
//2.配置WebApplicationContext
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
//把生成的webApplicationContext 設置為root webApplicationContext。
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
return this.context;
}
在上面的代碼中主要有兩個功能:
- (1)創(chuàng)建WebApplicationContext實例。
- (2)配置生成WebApplicationContext實例。
3.1.1 創(chuàng)建WebApplicationContext實例
進入CreateWebAPPlicationContext函數(shù)
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//得到ContextClass類,默認實例化的是XmlWebApplicationContext類
Class<?> contextClass = determineContextClass(sc);
//實例化Context類
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
進入determineContextClass函數(shù)。
protected Class<?> determineContextClass(ServletContext servletContext) {
// 此處CONTEXT_CLASS_PARAM = "contextClass"String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
//若設置了contextClass則使用定義好的ContextClass。
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
else {
//此處獲取的是在Spring源碼中ContextLoader.properties中配置的org.springframework.web.context.support.XmlWebApplicationContext類。
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
3.1.2 配置Web ApplicationContext
進入configureAndReFreshWebApplicaitonContext函數(shù)。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
//webapplicationContext設置servletContext.
wac.setServletContext(sc);
// 此處CONFIG_LOCATION_PARAM = "contextConfigLocation",即讀即取web.xm中配設置的contextConfigLocation參數(shù)值,獲得spring bean的配置文件.
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
//webApplicationContext設置配置文件路徑設。
wac.setConfigLocation(configLocationParam);
}
//開始處理bean
wac.refresh();
}
3.2 解析XML文件
上面wac變量聲明為ConfigurableWebApplicationContext類型,ConfigurableWebApplicationContext又繼承了WebApplicationContext。
WebApplication Context有很多實現(xiàn)類。
但從上面determineContextClass得知此處wac實際上是XmlWebApplicationContext類,因此進入XmlWebApplication類查看其繼承的refresh()方法。
沿方法調(diào)用棧一層層看下去。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//獲取beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 實例化所有聲明為非懶加載的單例bean
finishBeanFactoryInitialization(beanFactory);
}
}
獲取beanFactory。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//初始化beanFactory
refreshBeanFactory();
return beanFactory;
}
beanFactory初始化。
@Override
protected final void refreshBeanFactory() throws BeansException {
DefaultListableBeanFactory beanFactory = createBeanFactory();
//加載bean定義
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
加載bean。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//創(chuàng)建XmlBeanDefinitionReader實例來解析XML配置文件
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
initBeanDefinitionReader(beanDefinitionReader);
//解析XML配置文件中的bean。
loadBeanDefinitions(beanDefinitionReader);
}
讀取XML配置文件。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
//此處讀取的就是之前設置好的web.xml中配置文件地址
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
//調(diào)用XmlBeanDefinitionReader讀取XML配置文件
reader.loadBeanDefinitions(configLocation);
}
}
}
XmlBeanDefinitionReader讀取XML文件中的bean定義。
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
Resource resource = resourceLoader.getResource(location);
//加載bean
int loadCount = loadBeanDefinitions(resource);
return loadCount;
}
}
繼續(xù)查看loadBeanDefinitons函數(shù)調(diào)用棧,進入到XmlBeanDefinitioReader類的loadBeanDefinitions方法。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//獲取文件流
InputStream inputStream = encodedResource.getResource().getInputStream();
InputSource inputSource = new InputSource(inputStream);
//從文件流中加載定義好的bean。
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
}
最終將XML文件解析成Document文檔對象。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
//XML配置文件解析到Document實例中
Document doc = doLoadDocument(inputSource, resource);
//注冊bean
return registerBeanDefinitions(doc, resource);
}
3.3 解析bean
上一步完成了XML文件的解析工作,接下來將XML中定義的bean注冊到webApplicationContext,繼續(xù)跟蹤函數(shù)。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//使用documentRedder實例讀取bean定義
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
}
用BeanDefinitionDocumentReader對象來注冊bean。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
//讀取document元素
Element root = doc.getDocumentElement();
//真正開始注冊bean
doRegisterBeanDefinitions(root);
}
解析XML文檔。
protected void doRegisterBeanDefinitions(Element root) {
//預處理XML
preProcessXml(root);
//解析注冊bean
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
}
循環(huán)解析XML文檔中的每個元素。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//如果該元素屬于默認命名空間走此邏輯。Spring的默認namespace為:http://www.springframework.org/schema/beans“
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;
//對document中的每個元素都判斷其所屬命名空間,然后走相應的解析邏輯
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
//如果該元素屬于自定義namespace走此邏輯 ,比如AOP,MVC等。
delegate.parseCustomElement(root);
}
}
下面是默認命名空間的解析邏輯。
不明白Spring的命名空間的可以網(wǎng)上查一下,其實類似于package,用來區(qū)分變量來源,防止變量重名。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//解析import元素
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//解析alias元素
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//解析bean元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//解析beans元素
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
這里我們就不一一跟蹤,以解析bean元素為例繼續(xù)展開。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析bean
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 注冊bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
}
解析bean元素,最后把每個bean解析為一個包含bean所有信息的BeanDefinitionHolder對象。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
3.4 注冊bean
接下來將解析到的bean注冊到webApplicationContext中。接下繼續(xù)跟蹤registerBeanDefinition函數(shù)。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 獲取beanname
String beanName = definitionHolder.getBeanName();
//注冊bean
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 注冊bean的別名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
跟蹤registerBeanDefinition函數(shù),此函數(shù)將bean信息保存到到webApplicationContext的beanDefinitionMap變量中,該變量為map類型,保存Spring 容器中所有的bean定義。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//把bean信息保存到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
//把beanName 保存到List 類型的beanDefinitionNames屬性中
this.beanDefinitionNames.add(beanName);
}
3.5 實例化bean
Spring 實例化bean的時機有兩個。
一個是容器啟動時候,另一個是真正調(diào)用的時候。
如果bean聲明為scope=singleton且lazy-init=false,則容器啟動時候就實例化該bean(Spring 默認就是此行為)。否則在調(diào)用時候再進行實例化。
相信用過Spring的同學們都知道以上概念,但是為什么呢?
繼續(xù)從源碼角度進行分析,回到之前XmlWebApplication的refresh()方法。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//生成beanFactory,
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 實例化所有聲明為非懶加載的單例bean
finishBeanFactoryInitialization(beanFactory);
}
可以看到獲得beanFactory后調(diào)用了 finishBeanFactoryInitialization()方法,繼續(xù)跟蹤此方法。
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 初始化非懶加載的單例bean
beanFactory.preInstantiateSingletons();
}
預先實例化單例類邏輯。
public void preInstantiateSingletons() throws BeansException {
// 獲取所有注冊的bean
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 遍歷bean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//如果bean是單例且非懶加載,則獲取實例
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
getBean(beanName);
}
}
}
獲取bean。
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
doGetBean中處理的邏輯很多,為了減少干擾,下面只顯示了創(chuàng)建bean的函數(shù)調(diào)用棧。
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//創(chuàng)建bean
createBean(beanName, mbd, args);
}
創(chuàng)建bean。
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
return beanInstance;
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {
// 實例化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
//實例化bean
return instantiateBean(beanName, mbd);
}
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
//調(diào)用實例化策略進行實例化
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName,
}
判斷哪種動態(tài)代理方式實例化bean。
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
//使用JDK動態(tài)代理
if (bd.getMethodOverrides().isEmpty()) {
return BeanUtils.instantiateClass(constructorToUse);
}
else {
//使用CGLIB動態(tài)代理
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
不管哪種方式最終都是通過反射的形式完成了bean的實例化。
public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
}
3.6 獲取bean
我們繼續(xù)回到doGetBean函數(shù),分析獲取bean的邏輯。
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//獲取beanName
final String beanName = transformedBeanName(name);
Object bean
// 先檢查該bean是否為單例且容器中是否已經(jīng)存在例化的單例類
Object sharedInstance = getSingleton(beanName);
//如果已存在該bean的單例類
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else{
// 獲取父BeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
//先判斷該容器中是否注冊了此bean,如果有則在該容器實例化bean,否則再到父容器實例化bean
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
// 如果父容器有該bean,則調(diào)用父beanFactory的方法獲得該bean
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
//如果該bean有依賴bean,先實遞歸例化依賴bean。
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
registerDependentBean(dep, beanName);
getBean(dep);
}
}
//如果scope為Singleton執(zhí)行此邏輯
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
//調(diào)用創(chuàng)建bean方法
return createBean(beanName, mbd, args);
}
}
});
}
//如果scope為Prototype執(zhí)行此邏輯,每次獲取時候都實例化一個bean
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
prototypeInstance = createBean(beanName, mbd, args);
}
//如果scope為Request,Session,GolbalSession執(zhí)行此邏輯
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return createBean(beanName, mbd, args);
}
});
}
}
}
return (T) bean;
}
上面方法中首先調(diào)用getSingleton(beanName)方法來獲取單例bean,如果獲取到則直接返回該bean。方法調(diào)用棧如下:
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//從singletonObjects中獲取bean。
Object singletonObject = this.singletonObjects.get(beanName);
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
getSingleton方法先從singletonObjects屬性中獲取bean 對象,如果不為空則返回該對象,否則返回null。
那
singletonObjects保存的是什么?什么時候保存的呢?
回到doGetBean()函數(shù)繼續(xù)分析。如果singletonObjects沒有該bean的對象,進入到創(chuàng)建bean的邏輯。處理邏輯如下:
//獲取父beanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
//如果該容器中沒有注冊該bean,且父容器不為空,則去父容器中獲取bean后返回
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
下面是判斷容器中有沒有注冊bean的邏輯,此處beanDefinitionMap相信大家都不陌生,在注冊bean的流程里已經(jīng)說過所有的bean信息都會保存到該變量中。
public boolean containsBeanDefinition(String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
return this.beanDefinitionMap.containsKey(beanName);
}
如果該容器中已經(jīng)注冊過bean,繼續(xù)往下走。先獲取該bean的依賴bean,如果镩子依賴bean,則先遞歸獲取相應的依賴bean。
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
registerDependentBean(dep, beanName);
getBean(dep);
}
}
依賴bean創(chuàng)建完成后,接下來就是創(chuàng)建自身bean實例了。
獲取bean實例的處理邏輯有三種,即Singleton、Prototype、其它(request、session、global session),下面一一說明。
3.6.1 Singleton
如果bean是單例模式,執(zhí)行此邏輯。
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
//創(chuàng)建bean回調(diào)
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
獲取單例bean,如果已經(jīng)有該bean的對象直接返回。如果沒有則創(chuàng)建單例bean對象,并添加到容器的singletonObjects Map中,以后直接從singletonObjects直接獲取bean。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
//如果singletonObjects中沒有該bean
if (singletonObject == null) {
//回調(diào)參數(shù)傳進來的ObjectFactory的getObject方法,即調(diào)用createBean方法創(chuàng)建bean實例
singletonObject = singletonFactory.getObject();
//置新創(chuàng)建單例bean標志位為true。
newSingleton = true;
if (newSingleton) {
//如果是新創(chuàng)建bean,注冊新生成bean對象
addSingleton(beanName, singletonObject);
}
}
//返回獲取的單例bean
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
把新生成的單例bean加入到類型為MAP 的singletonObjects屬性中,這也就是前面singletonObjects()方法中獲取單例bean時從此Map中獲取的原因。
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//把新生成bean對象加入到singletonObjects屬性中。
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.registeredSingletons.add(beanName);
}
}
3.6.2 Prototype
Prototype是每次獲取該bean時候都新建一個bean,因此邏輯比較簡單,直接創(chuàng)建一個bean后返回。
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
//創(chuàng)建bean
prototypeInstance = createBean(beanName, mbd, args);
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
3.6.3 request、session、global session
else {
//獲取該bean的scope
String scopeName = mbd.getScope();
//獲取相應scope
final Scope scope = this.scopes.get(scopeName);
//獲取相應scope的實例化對象
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return createBean(beanName, mbd, args);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
從相應scope獲取對象實例。
public Object get(String name, ObjectFactory<?> objectFactory) {
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
//先從指定scope中獲取bean實例,如果沒有則新建,如果已經(jīng)有直接返回
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
//回調(diào)函數(shù)調(diào)用createBean創(chuàng)建實例
scopedObject = objectFactory.getObject();
//創(chuàng)建實例后保存到相應scope中
attributes.setAttribute(name, scopedObject, getScope());
}
}
return scopedObject;
}
判斷scope,獲取實例函數(shù)邏輯。
public Object getAttribute(String name, int scope) {
//scope是request時
if (scope == SCOPE_REQUEST) {
//從request中獲取實例
return this.request.getAttribute(name);
}
else {
PortletSession session = getSession(false);
if (session != null) {
//scope是globalSession時,從application中獲取實例
if (scope == SCOPE_GLOBAL_SESSION) {
//從globalSession中獲取實例
Object value = session.getAttribute(name, PortletSession.APPLICATION_SCOPE);
return value;
}
else {
//從session中獲取實例
Object value = session.getAttribute(name);
return value;
}
}
return null;
}
}
在相應scope中設置實例函數(shù)邏輯。
public void setAttribute(String name, Object value, int scope) {
if (scope == SCOPE_REQUEST) {
this.request.setAttribute(name, value);
}
else {
PortletSession session = getSession(true);
if (scope == SCOPE_GLOBAL_SESSION) {
session.setAttribute(name, value, PortletSession.APPLICATION_SCOPE);
}
else {
session.setAttribute(name, value);
}
}
}
以上就是Spring bean從無到有的整個邏輯。
4. 小結
從源碼角度分析 bean的實例化流程到此基本接近尾聲了。
回到開頭的問題,ContextLoaderListener中初始化的WebApplicationContext到底是什么呢?
通過源碼的分析我們知道WebApplicationContext負責了bean的創(chuàng)建、保存、獲取。其實也就是我們平時所說的IOC容器,只不過名字表述不同而已。
5. 尾聲
本文主要是講解了XML配置文件中bean的解析、注冊、實例化。對于其它命名空間的解析還沒有講到,后續(xù)的文章中會一一介紹。
希望通過本文讓大家在以后使用Spring的過程中有“一切盡在掌控之中”的感覺,而不僅僅是稀里糊涂的使用。
如果想獲得更多,歡迎關注公眾號:七分熟pizza
公眾號里我會分享更多技術以及職場方面的經(jīng)驗,大家有什么問題也可以直接在公眾號向我提問交流。