最近在使用Spring MVC過程當中遇到了一些問題,網上搜索很多帖子後雖然找到了答案和解決方法,但這些答案大部分都只是給告終論,並無說明具體緣由,感受老是有點不太滿意。html
更重要的是這些所謂的結論大可能是抄來抄去,基本源自一家,真實性也有待考證。java
要成爲一名優秀的碼農,不只能熟練的複製粘貼,更要有打破砂鍋問到底的精神,達到知其然也知其因此然的境界。node
那做爲程序員怎麼能知其因此然呢?git
答案就是閱讀源代碼!程序員
此處請你們心裏默讀三遍。github
用過Spring 的人都知道其核心就是IOC和AOP,所以要想了解Spring機制就得先從這兩點入手,本文主要經過對IOC部分的機制進行介紹。web
在開始閱讀以前,先準備好如下實驗材料。spring
Spring 5.0源碼(github.com/spring-proj…)api
IDE:Intellij IDEAbash
IDEA 是一個優秀的開發工具,若是還在用Eclipse的建議切換到此工具進行。
IDEA有不少的快捷鍵,在分析過程當中建議你們多用Ctrl+Alt+B快捷鍵,能夠快速定位到實現函數。
Spring bean的加載主要分爲如下6步:
查看源碼第一步是找到程序入口,再以入口爲突破口,一步步進行源碼跟蹤。
Java Web應用中的入口就是web.xml。
在web.xml找到ContextLoaderListener ,此Listener負責初始化Spring IOC。
contextConfigLocation參數設置了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初始化時候調用
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext();
}
//servletContext銷燬時候調用
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
}
}
複製代碼
從源碼看出此Listener主要有兩個函數,一個負責初始化WebApplicationContext,一個負責銷燬。
繼續看initWebApplicationContext函數。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
//初始化Spring容器時若是發現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.建立webApplicationContext實例
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
//2.配置WebApplicationContext
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
//3.把生成的webApplicationContext 設置爲root webApplicationContext。
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
return this.context;
}
複製代碼
在上面的代碼中主要有兩個功能:
進入CreateWebAPPlicationContext函數
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
//獲得ContextClass類,默認實例化的是XmlWebApplicationContext類
Class<?> contextClass = determineContextClass(sc);
//實例化Context類
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}
複製代碼
進入determineContextClass函數。
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());
}
複製代碼
進入configureAndReFreshWebApplicaitonContext函數。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
//webapplicationContext設置servletContext.
wac.setServletContext(sc);
// 此處CONFIG_LOCATION_PARAM = "contextConfigLocation",即讀即取web.xm中配設置的contextConfigLocation參數值,得到spring bean的配置文件.
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
//webApplicationContext設置配置文件路徑設。
wac.setConfigLocation(configLocationParam);
}
//開始處理bean
wac.refresh();
}
複製代碼
上面wac變量聲明爲ConfigurableWebApplicationContext類型,ConfigurableWebApplicationContext又繼承了WebApplicationContext。
WebApplication Context有不少實現類。 但從上面determineContextClass得知此處wac其實是XmlWebApplicationContext類,所以進入XmlWebApplication類查看其繼承的refresh()方法。
沿方法調用棧一層層看下去。
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 {
//建立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) {
//調用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;
}
}
複製代碼
繼續查看loadBeanDefinitons函數調用棧,進入到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);
}
複製代碼
上一步完成了XML文件的解析工做,接下來將XML中定義的bean註冊到webApplicationContext,繼續跟蹤函數。
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);
}
複製代碼
循環解析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的命名空間的能夠網上查一下,其實相似於package,用來區分變量來源,防止變量重名。
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元素爲例繼續展開。
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);
}
複製代碼
接下來將解析到的bean註冊到webApplicationContext中。接下繼續跟蹤registerBeanDefinition函數。
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函數,此函數將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);
}
複製代碼
Spring 實例化bean的時機有兩個。
一個是容器啓動時候,另外一個是真正調用的時候。
若是bean聲明爲scope=singleton且lazy-init=false,則容器啓動時候就實例化該bean(Spring 默認就是此行爲)。不然在調用時候再進行實例化。
相信用過Spring的同窗們都知道以上概念,可是爲何呢?
繼續從源碼角度進行分析,回到以前XmlWebApplication的refresh()方法。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//生成beanFactory,
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 實例化全部聲明爲非懶加載的單例bean
finishBeanFactoryInitialization(beanFactory);
}
複製代碼
能夠看到得到beanFactory後調用了 finishBeanFactoryInitialization()方法,繼續跟蹤此方法。
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中處理的邏輯不少,爲了減小干擾,下面只顯示了建立bean的函數調用棧。
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
//建立bean
createBean(beanName, mbd, args);
}
複製代碼
建立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) {
//調用實例化策略進行實例化
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName,
}
複製代碼
判斷哪一種動態代理方式實例化bean。
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
//使用JDK動態代理
if (bd.getMethodOverrides().isEmpty()) {
return BeanUtils.instantiateClass(constructorToUse);
}
else {
//使用CGLIB動態代理
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
複製代碼
無論哪一種方式最終都是經過反射的形式完成了bean的實例化。
public static <T> T instantiateClass(Constructor<T> ctor, Object... args)
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
}
複製代碼
咱們繼續回到doGetBean函數,分析獲取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是否爲單例且容器中是否已經存在例化的單例類
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,則調用父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執行此邏輯
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
//調用建立bean方法
return createBean(beanName, mbd, args);
}
}
});
}
//若是scope爲Prototype執行此邏輯,每次獲取時候都實例化一個bean
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
prototypeInstance = createBean(beanName, mbd, args);
}
//若是scope爲Request,Session,GolbalSession執行此邏輯
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;
}
複製代碼
上面方法中首先調用getSingleton(beanName)方法來獲取單例bean,若是獲取到則直接返回該bean。方法調用棧以下:
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()函數繼續分析。若是singletonObjects沒有該bean的對象,進入到建立bean的邏輯。處理邏輯以下:
//獲取父beanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
//若是該容器中沒有註冊該bean,且父容器不爲空,則去父容器中獲取bean後返回
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
複製代碼
下面是判斷容器中有沒有註冊bean的邏輯,此處beanDefinitionMap相信你們都不陌生,在註冊bean的流程裏已經說過全部的bean信息都會保存到該變量中。
public boolean containsBeanDefinition(String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
return this.beanDefinitionMap.containsKey(beanName);
}
複製代碼
若是該容器中已經註冊過bean,繼續往下走。先獲取該bean的依賴bean,若是鑹子依賴bean,則先遞歸獲取相應的依賴bean。
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
registerDependentBean(dep, beanName);
getBean(dep);
}
}
複製代碼
依賴bean建立完成後,接下來就是建立自身bean實例了。
獲取bean實例的處理邏輯有三種,即Singleton、Prototype、其它(request、session、global session),下面一一說明。
若是bean是單例模式,執行此邏輯。
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
//建立bean回調
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
複製代碼
獲取單例bean,若是已經有該bean的對象直接返回。若是沒有則建立單例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) {
//回調參數傳進來的ObjectFactory的getObject方法,即調用createBean方法建立bean實例
singletonObject = singletonFactory.getObject();
//置新建立單例bean標誌位爲true。
newSingleton = true;
if (newSingleton) {
//若是是新建立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);
}
}
複製代碼
Prototype是每次獲取該bean時候都新建一個bean,所以邏輯比較簡單,直接建立一個bean後返回。
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
//建立bean
prototypeInstance = createBean(beanName, mbd, args);
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
複製代碼
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實例,若是沒有則新建,若是已經有直接返回
Object scopedObject = attributes.getAttribute(name, getScope());
if (scopedObject == null) {
//回調函數調用createBean建立實例
scopedObject = objectFactory.getObject();
//建立實例後保存到相應scope中
attributes.setAttribute(name, scopedObject, getScope());
}
}
return scopedObject;
}
複製代碼
判斷scope,獲取實例函數邏輯。
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中設置實例函數邏輯。
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從無到有的整個邏輯。
從源碼角度分析 bean的實例化流程到此基本接近尾聲了。
回到開頭的問題,ContextLoaderListener中初始化的WebApplicationContext究竟是什麼呢?
經過源碼的分析咱們知道WebApplicationContext負責了bean的建立、保存、獲取。其實也就是咱們平時所說的IOC容器,只不過名字表述不一樣而已。
本文主要是講解了XML配置文件中bean的解析、註冊、實例化。對於其它命名空間的解析尚未講到,後續的文章中會一一介紹。
但願經過本文讓你們在之後使用Spring的過程當中有「一切盡在掌控之中」的感受,而不只僅是稀裏糊塗的使用。
想要了解更多,關注公衆號:七分熟pizza