前言html
做爲一個Java開發者,工做了幾年後,愈加覺力有點不從心了,技術的世界實在是太過於遼闊了,接觸的東西越多,越感到史無前例的恐慌。java
天天搗鼓這個搗鼓那個,結果回過頭來,才發現這個也不通,那個也不精。就連我吃飯的傢伙Java,如今想一想,其實我根本就不瞭解。程序員
但是每當編寫簡歷的時候,總想把工做經驗、工做年限寫的長一點,半年寫成一年,一年寫成兩年。但是每當有人問我技術原理的時候,又會想,web
個人工做時間要是短一點的話,答不上來是否是就不會這麼丟臉。面試
還記得剛工做不久,就在項目中使用過Spring了,可是那個時候,只是照着別人寫的代碼,照葫蘆畫瓢似得寫着本身的代碼,畫瓢花久了,寫的熟練了,心中就開始竊喜,spring
居然也就敢厚着臉皮說,我也會Spring了,簡歷上就絕不猶豫的寫上:瞭解Spring。(真是臭不要臉啊)設計模式
Spring真的是很流行,作過了好多項目,基本上都或多或少的使用了Spring框架或者集成了Spring框架,因而乎,不管進入了哪一個項目中,居然也都駕輕就熟。spring-mvc
而後就有點飄飄然,領導還給安排帶幾個新人,開發時,還得意的給新人們講解,這個註解這樣使用,那個配置那樣配置。可是,其實如何使用這個東西,只要是程序員,短短几天也都會了。緩存
幾天後,新人就能與你幹一樣的活了,你這個老人與新人的區別在哪裏呢?其實這時,我有點慌了。服務器
平時遊蕩於各個技術交流羣,發現羣裏面其實有好多仍是在校生,就開始鑽研各類技術難題。回頭再想一想,我當時還在學校的時候在幹嗎呢:打遊戲、看小說、睡懶覺、泡校內網、約妹子、耗時間...
人家還在學生時期,就開始閱讀Spring源碼了。而我呢,居然都用了好幾年了,都歷來沒有提起過閱讀源碼的念頭。
後來有一次面試,人家問:看你也有幾年工做經驗了,說一說Spring的原理吧、IOC、DI、AOP...再說一說,從一個請求開始,一直到獲得響應,Spring都幹了些什麼?
個人個親孃啊,你到底在說什麼,我怎麼聽不懂。我突然想起了一首歌:
我想了好久,我開始慌了, 是否是我又作錯了什麼, 你哭着對我說,童話裏都是騙人的,你不多是我要的程序猿
岔題了...
亡羊補牢,痛定思痛,總而言之,就讀一下Spring源碼(3.1版本)看看吧。
讀讀讀讀讀...
對於沒有任何源碼閱讀經驗的人,並且大局觀總體概念不好的人來講,源碼真的是太難讀了,可能仍是人笨吧。因此我只能採起老辦法,所謂書讀百遍,其義自現,讀源碼應該也是一樣的道理。
到開始寫本篇筆記開始,前先後後已經花了整整3周時間,期間各類debug,打了上百個斷點,不厭其煩的一遍又一遍跟蹤跟進,從服務器啓動開始,
從HttpServletBean的init()進入,再到initWebApplicationContext(),再到refresh(),再到refreshBeanFactory(),再到finishRefresh(),直到服務器啓動成功。不知道讀了多少遍,
可是源碼的東西實在的太多了,想要徹底讀懂,徹底理清頭緒,還差很遠啊。因此我只重點關注了兩塊內容,就是bean的定位加載解析註冊、bean的實例化兩大塊內容,其餘地方稍做了解,沒有太過深刻。
整個容器的啓動流程,都在AbstractApplicationContext的refresh()的模板方法中了。
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 // Prepare this context for refreshing. 4 prepareRefresh(); 5 6 // Tell the subclass to refresh the internal bean factory. 7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 8 9 // Prepare the bean factory for use in this context. 10 prepareBeanFactory(beanFactory); 11 12 try { 13 // Allows post-processing of the bean factory in context subclasses. 14 postProcessBeanFactory(beanFactory); 15 16 // Invoke factory processors registered as beans in the context. 17 invokeBeanFactoryPostProcessors(beanFactory); 18 19 // Register bean processors that intercept bean creation. 20 registerBeanPostProcessors(beanFactory); 21 22 // Initialize message source for this context. 23 initMessageSource(); 24 25 // Initialize event multicaster for this context. 26 initApplicationEventMulticaster(); 27 28 // Initialize other special beans in specific context subclasses. 29 onRefresh(); 30 31 // Check for listener beans and register them. 32 registerListeners(); 33 34 // Instantiate all remaining (non-lazy-init) singletons. 35 finishBeanFactoryInitialization(beanFactory); 36 37 // Last step: publish corresponding event. 38 finishRefresh(); 39 } 40 41 catch (BeansException ex) { 42 // Destroy already created singletons to avoid dangling resources. 43 destroyBeans(); 44 45 // Reset 'active' flag. 46 cancelRefresh(ex); 47 48 // Propagate exception to caller. 49 throw ex; 50 } 51 } 52 }
其實,我並無上來就看源碼,而是先從看書開始,稍微瞭解,知道了一些關鍵點,關鍵流程,本身產生了一堆疑問,而後帶着疑問去讀源碼,讀着讀着,發現有些疑問就這麼解決了。
看書:主要以《SPRING技術內幕:深刻解析SPRING架構與設計原理》爲主,僅僅纔看了前兩章節,關於Spring Framework的核心實現,AOP什麼,事務控制什麼的都還沒看。
看博客:看源碼過程當中,有些不理解的,就上網查博客,而後再回頭讀源碼。以爲寫得還能夠的博客文章我記錄了下來,供之後參考(但願管理員不要由於有外部連接,就把我踢掉了)。
SpringMVC源碼剖析(一)- 從抽象和接口提及 - 相見歡的我的空間(隨便一提,這位大神的Spring系列博客真是寫得太好了)
正如上文所說,由於水平有限,本篇隨筆並不是是解析源碼的文章,由於源碼不少看不懂的地方並不敢妄下斷言, 網上各類解析源碼,畫UML圖,時序圖(我不會畫圖%>_<%)的文章也有不少,
我是抱着解決疑問的態度來分析讀源碼的,另外在大神眼中,有些問題看起來可能問的很愚蠢,可是誰讓我是菜鳥呢。
以上是個人心路歷程,不看也罷。
那麼,列出有疑問的地方(目前主要針對IOC相關部分、問題將不斷補充)
(補充:我用來閱讀而且debug的demo是基於註解配置的,另外如下說明都純屬我的謬論,若是錯漏,請予以批評指正。)
問題1:我爲何要使用Spring?
問題2:Spring怎麼這麼聰明,知道哪些bean須要被實例化?
問題3:Spring中Bean是何時被實例化的?
問題4:依賴的bean是什麼時候被注入的?
問題5:bean的單例模式?原型模式是什麼東西?分別是什麼時候被實例化的?
問題6:採用註解注入時,爲何只聲明接口就能夠將其實現類注入?
問題7:採用註解注入時,接口與實現類爲什麼都能注入成功?區別?
問題8:加上了autowired的屬性是何時實例化的?
問題9:網絡上常常說的「第一次使用bean的時候實例化該bean」是什麼意思?
問題10:發送一個請求,是怎麼定位到具體的Controller的某一個方法的?
問題11:HandlerMapping、HandlerAdater與Controller究竟是什麼關係?
問題1:我爲何要使用Spring?
這個問題其實比較尷尬,由於其實我入行比較晚, 這個時候,已經有一大波比較成熟的框架體系了,早期的什麼繁瑣的臃腫的框架基本上沒用過幾個,再加上項目經驗比較單一,用來用去就那麼幾個框架,用起來都大差不差,
用的熟練了,感受都差很少。因此說,徹底沒有能力來橫向比較,誰誰誰架構合理,誰誰誰擴展性強,只能勉強說這個框架的那種寫法挺方便,比起單純的Servlet寫法要方便之類的....其餘的,我都呵呵就行了。
若是你要強行問我爲何使用Spring,那我只能回答你:由於你們都在用啊,呵呵...
問題2:Spring怎麼這麼聰明,知道哪些bean須要被實例化?
什麼控制反轉、依賴注入,說到底就是程序在須要使用一個bean的時候,Spring框架確保該bean已經被實例化了,能夠直接拿來使用。那麼這時候,我想要知道,Spring是如何知道哪些bean須要實例化?
其實程序總歸是程序,程序是很笨的,它並不知道要實例化哪些東西,除非你告訴它。
首先須要配置DispatcherServlet,這是web應用的總入口,也能夠說是中央處理器,就是經過它,一步一步的啓動Spring容器的。
在web.xml中
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring/readspring-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
另外還須要配置另一個文件:readspring-servlet.xml,這個東西叫作上下文,主要是告訴Spring,在啓動的時候幹些什麼事情,啓動哪些功能,實例化哪些Bean。
上一個最簡單的配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <!-- 掃描指定目錄 --> <context:component-scan base-package="com.readspring" /> <!-- 不攔截靜態資源 --> <mvc:default-servlet-handler /> <!-- 啓動註解支持 --> <mvc:annotation-driven /> <!-- 配置視圖解析器 --> <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> </beans>
我不逐一解讀每句話的意思,仍是針對上面的問題,Spring如何知道哪些bean要被實例化?
或者說的具體一點,Spring是如何知道要實例化咱們寫的Controller、Service、Dao的。
是這段配置告訴它的。
<!-- 掃描指定目錄 --> <context:component-scan base-package="com.readspring" />
從字面意思能夠看出,component-scan就是組件掃描的意思,而後有一個base-package,告訴它須要掃描的文件路徑。
組件自動掃描是在Spring2.5加上的,在一個稍大的項目中一般會有上百個組件,若是都使用XML的bean定義來配置組件的話,顯然會增長配置文件的體積,
查找及維護也不方便,而Spring2.5就爲咱們引入了組件自動掃描機制,它能夠在classpath下尋找標註了@Service、@Repository、@Controller、@Component註解的類
並把這些類歸入Spring容器中管理,它的做用和在XML中使用bean節點配置組件是同樣的。
另外補充說明,既然是component掃描,爲何標註了@Service等也會被掃描到,能夠看一下源碼。
Service註解自己也被標註了@Component註解,因此說能夠被掃描到,至關於繼承自@Component。
下面,我大概的描述一下,容器啓動的過程當中,如何根據配置,來掃描指定類的。
容器啓動過程當中,首先調用DispatcherSerlvet的init方法,init方法內部根據web.xml的配置,讀取配置的上下文readspring-servlet.xml,而後逐句解析該上下文,
當它讀取到context:component-scan標籤時,就啓動對應的解析器,也能夠叫作掃描器,對應的Class爲:ComponentScanBeanDefinitionParser
有這麼一個對應關係的Class,可讓Spring知道,爲何讀取到這個標籤,就實例化ComponentScanBeanDefinitionParser這個類。
public class ContextNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } }
看一下這個類ComponentScanBeanDefinitionParser中調用了doScan方法,能夠看到傳了一個參數:basePackages,就是咱們配置的路徑。
public BeanDefinition parse(Element element, ParserContext parserContext) { String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // Actually scan for bean definitions and register them. ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
具體的掃描邏輯,在這個類中ClassPathScanningCandidateComponentProvider.findCandidateComponents(...)
這樣,就讀取到了須要被實例化的全部類,以後這些類的信息會被封裝成一個一個的BeanDefinition,而後保存到DefaultListableBeanFactory的beanDefinitionMap中供後續使用。
/** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
這個問題算是解決了。
問題3:Spring中Bean是何時被實例化的?
通常開發web應用時,有controller、service、dao等不一樣的bean,這些bean究竟是怎麼實例化的?何時被實例化的?有順序嗎?
首先回答該問題:全部的這些Bean,在容器啓動的時候,已經所有被實例化了。
下面看代碼一探究竟,還得看模板方法,AbstractApplicationContext.refresh(),其中有這麼一個方法
// Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);
看註釋知道:實例化剩下的全部單例。
Spring中的Bean默認都是單例的,除非顯示的聲明爲prototype。
剩下的(remaining)的意思是有一些Spring自身的處理器、解析器等Bean不是在這裏實例化的,咱們本身編寫的常規類Controller、Service等所有都是在此處實例化。
下面我用僞代碼的形式簡要的說明一下實例化的過程
以TestController爲例子
Class TestController {
@Autowired
private ITestService testService;
...
}
// 首先循環全部的BeanDefinition列表
for(String beanName : 以前獲取到的beanDefiniton的List){
String beanName = "testController"; // 以TestController爲例子
// 調用getbean方法
getBean(beanName){
// 檢查是否已經被實例化
Object instance = getSingleton(beanName);
if (instance == null) {
// 若是沒有,準備實例化
bean = createBean(beanName){
// 執行實例化過程,正式實例化就是在這個方法中
bean = createBeanInstance(){
// 這裏具體的實例化是利用cglib類庫,經過Java反射原理,構造函數實例化方式實例化
ReflectionUtils.makeAccessible(ctor);
Constructor.newInstance(args);
}
// 組裝Bean,所謂的組裝就是把該bean的屬性給設置一下,如testService
populateBean(bean){
// 以TestController爲例子,這個方法裏面,testService的實例的引用會被注入(Inject)到TestConroller的實例中的這個屬性testService中
// 若是testService的實例不存在,就會遞歸調用getBean()方法,直到一個沒有任何依賴的Bean爲止。
Object serviceInstance = getBean(testService);
// 注入屬性實例,至關於 testConroller.testService = serviceInstance;(依賴注入就是在這裏實現的)
inject(serviceInstance);
}
// 實例化成功後,實例會被保存到singletonObjects Map中
addSingleton(beanName, serviceInstance);
}
}
}
}
(此處省略了大量細節,只關注總體實現流程)
全部的Bean被實例化後,會被存入這個Map中供後續使用。
DefaultSingletonBeanRegistry.java
/** Cache of singleton objects: bean name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();
從上面的說明中,大致能夠了解實例化Bean的流程,下面再來看一下源碼實例化的一些具體細節實現。
1.Bean的實例化
首先看AbstractAutowireCapableBeanFactory.createBeanInstance(...)
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { // Make sure bean class is actually resolved at this point. Class beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } // Need to determine the constructor... Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd); }
通過一些列的必要性驗證,最終調用了instantiateBean(...),有兩個參數:一個beanName,一個是該bean的定義信息
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return getInstantiationStrategy().instantiate(mbd, beanName, parent); } }, getAccessControlContext()); } else { beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }
在這裏能夠看到獲取了實例化策略:getInstantiationStrategy(),點進去能夠看到,默認採起的是Cglib的實例化策略。
/** Strategy for creating bean instances */ private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
而後繼續跟進instantiate方法,
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. if (beanDefinition.getMethodOverrides().isEmpty()) { Constructor<?> constructorToUse; synchronized (beanDefinition.constructorArgumentLock) { constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class clazz = beanDefinition.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() { public Constructor run() throws Exception { return clazz.getDeclaredConstructor((Class[]) null); } }); } else { constructorToUse = clazz.getDeclaredConstructor((Class[]) null); } beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Exception ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(beanDefinition, beanName, owner); } }
能夠看到,首先獲取Bean的Class對象,而後根據Class對象獲取構造器,最終調用BeanUtils.instantiateClass方法。
通過了一層又一層的嵌套調用,終於看到了真正的Bean實例化方式,經過Java的反射機制,根據Class的默認構造器實例化Bean對象。
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException { Assert.notNull(ctor, "Constructor must not be null"); try { ReflectionUtils.makeAccessible(ctor); return ctor.newInstance(args); } catch (InstantiationException ex) { throw new BeanInstantiationException(ctor.getDeclaringClass(), "Is it an abstract class?", ex); } catch (IllegalAccessException ex) { throw new BeanInstantiationException(ctor.getDeclaringClass(), "Is the constructor accessible?", ex); } catch (IllegalArgumentException ex) { throw new BeanInstantiationException(ctor.getDeclaringClass(), "Illegal arguments for constructor", ex); } catch (InvocationTargetException ex) { throw new BeanInstantiationException(ctor.getDeclaringClass(), "Constructor threw exception", ex.getTargetException()); } }
2.依賴注入
仍是參考以前的例子
Class TestController {
@Autowired
private ITestService testService;
...
}
TestController依賴於testService,若是想要正常使用TestController,首先TestController須要被實例化,而後其屬性testService也須要被實例化。
經過以前1.Bean的實例化,已經知道TestController是如何被實例化的,可是儘管TestController有了實例,可是若是你Debug會發現,其屬性值testService
此時仍然是testService == null,這個時候就須要把testService的實例(確切的說,應該是實例的引用),像「打針」同樣,注入到TestController實例的testService屬性中。
這段邏輯發生在上面僞代碼的populateBean(...)方法中,具體在AbstractAutowireCapableBeanFactory.populateBean(...)
在這個方法中,有這麼一段
if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } applyPropertyValues(beanName, mbd, bw, pvs);
由於我是採用的@Autowired註解注入的方式,因此這次實際上調用的是AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(...)方法
@Override public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { // 根據bean的Class對象獲取其對於的Field以及method InjectionMetadata metadata = findAutowiringMetadata(bean.getClass()); try {
// 調用注入方法 metadata.inject(bean, beanName, pvs); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } return pvs; }
由於可能有多個須要注入的屬性,因此須要循環注入
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable { if (!this.injectedElements.isEmpty()) { boolean debug = logger.isDebugEnabled(); for (InjectedElement element : this.injectedElements) { if (debug) { logger.debug("Processing injected method of bean '" + beanName + "': " + element); } element.inject(target, beanName, pvs); } } }
執行真正的注入(此處獲取testService實例時,若是實例不存在,將會觸發獲取testService實例的遞歸調用)
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Field field = (Field) this.member; try { Object value;
// 首先獲取須要注入的屬性的實例對象 if (this.cached) { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else { DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required); Set<String> autowiredBeanNames = new LinkedHashSet<String>(1); TypeConverter typeConverter = beanFactory.getTypeConverter(); value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter); synchronized (this) { if (!this.cached) { if (value != null || this.required) { this.cachedFieldValue = descriptor; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName); } } } } else { this.cachedFieldValue = null; } this.cached = true; } } }
// 利用Java反射機制,將filed的對象注入bean的屬性當中
// 至關於執行了 testController.setTestService(testService)
if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } } catch (Throwable ex) { throw new BeanCreationException("Could not autowire field: " + field, ex); } } }
這就注入好了。
問題4:依賴的bean是什麼時候被注入的?
這個問題其實問題3的2.依賴注入中已經解答了,當其父Bean在實例化後,調用組裝Bean的時候(populateBean方法),執行了依賴Bean的實例化以及注入操做。
問題5:bean的單例?多例是什麼東西?分別是什麼時候被實例化的?
首先來看看什麼是Spring的scope
目前,scope的取值有5種。
在Spring 2.0以前,有singleton和prototype兩種;
在Spring 2.0以後,爲支持web應用的ApplicationContext,推出另外三種:request、session和global session類型。
1. singleton (單一實例)
一個容器中只存在一個實例,全部對該類型bean的依賴都引用這一單一實例。此外,singleton類型的bean定義,從容器啓動,到他第一次被請求而實例化開始,只要容器不銷燬或退出,該類型的bean的單一實例就會一直存活。
SpringMVC中,默認都是採用singleton模式。
2. prototype(多例)
spring容器在進行輸出prototype的bean 對象時,會每次都從新生成一個新的對象給請求方,雖然這種類型的對象的實例化以及屬性設置等工做都是由容器負責的,可是隻要準備完畢,
而且對象實例返回給請求方以後,容器就不在擁有當前對象的引用,請求方須要本身負責當前對象後繼生命週期的管理工做,包括該對象的銷燬。
3. request 、session和global session
他們只適用於web程序,一般是和XmlWebApplicationContext共同使用。
能夠參考一下這篇博文:Spring對象生存週期(Scope)的分析
下面看一下源碼:AbstractBeanFactory.doGetBean(...),能夠看到,在實例化Bean的時候,首先判斷了該bean的scope範圍,
單例、多例、仍是其餘範圍,而後最終都調用了createBean方法,因此說無論是什麼做用範圍,其實例化的過程都是同樣的,能夠參考上面的說明。
另外能夠看到,單例時,從getSingleton(...)中取值,這個方法中會緩存全部已經實例化的單例Bean。而多例時,能夠看到每次都是直接調用的createBean方法,返回了新生成Bean。
protected <T> T doGetBean( final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException { ...// Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() { public Object getObject() throws BeansException { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() { public Object getObject() throws BeansException { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } } }); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; " + "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } ...return (T) bean; }
問題6:採用註解注入時,爲何只聲明接口就能夠將其實現類注入?
看以下例子:
@Controller Class TestController { @Autowired private ITestService testService; ... } @Service public class TestServiceImpl implements ITestService { ... }
通常狀況下,採用非註解方式注入的時候,都須要顯式的聲明變量與實現類的對應關係,相似於
<bean id="testService" class="com.readspring.service.impl.TestServiceImpl" />
可是在基於註解的配置中,並不須要這段配置卻仍然能夠正常的實現注入。
例如:在TestController中,其成員變量testService被聲明爲接口類型:ITestService,可是Spring在實例化TestController時,
卻能夠將TestServiceImpl的實例成功注入到testService中。
那麼,使用註解方式配置時,Spring是如何根據接口找到其實現類的呢?
答:Spring主要利用了Class.isAssignableFrom()方法來實現接口與實現類的匹配。
Class1.isAssignableFrom(Class2):若是Class1是Class2自己或者是其接口或者父類,則返回true,不然返回false;
以testService的實例化爲例,當Spring容器啓動的時候,首先會讀取配置文件,通過定位、加載、解析、註冊,最終把標註了註解的類(例如:標註了@Service的TestServiceImpl)註冊到BeanDefination的Map中而且緩存。
而後,在實例化testService的時候,Spring此刻並不知道testService的實現類是哪一個,而是循環遍歷BeanDefinitionMap中的Bean,逐一與ITestService接口進行類型匹配,一旦匹配上就認定爲其實現類,並對其進行實例化。
用僞代碼表示就是:
for(BeanDefinition bean: BeanDefinitionList) {
Class testServiceImplClass = Class.forName(bean.getBeanClassName());
if (ITestService.class.isAssignableFrom(testServiceImplClass)) {
// 成功定位到ITestService接口的實現類
return testServiceImplClass;
}
}
以上這段處理主要是發生在依賴注入階段(請參考以前的2.依賴注入)。
在以前依賴注入的描述中,側重描述了屬性注入其實就是經過Java反射來實現的,並無講解如何得到注入的實例,請看下面紅色代碼。
@Override protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Field field = (Field) this.member; try { Object value; if (this.cached) { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else { DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required); Set<String> autowiredBeanNames = new LinkedHashSet<String>(1); TypeConverter typeConverter = beanFactory.getTypeConverter();
// 根據接口定位到實現類的過程發生此處 value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter); synchronized (this) { if (!this.cached) { if (value != null || this.required) { this.cachedFieldValue = descriptor; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName); } } } } else { this.cachedFieldValue = null; } this.cached = true; } } } if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } } catch (Throwable ex) { throw new BeanCreationException("Could not autowire field: " + field, ex); } }
繼續跟進,定位到DefaultListableBeanFactory.doResolveDependency(...),本方法主要判斷屬性類型,根據不一樣的類型作相應處理。
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { ... if (type.isArray()) { ... } else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { ... } else if (Map.class.isAssignableFrom(type) && type.isInterface()) { ... } else {
// Find bean instances that match the required type.
// 根據實際的類型找到對應的實例 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(type, "", descriptor); } return null; } if (matchingBeans.size() > 1) { String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor); if (primaryBeanName == null) { throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " + matchingBeans.size() + ": " + matchingBeans.keySet()); } if (autowiredBeanNames != null) { autowiredBeanNames.add(primaryBeanName); } return matchingBeans.get(primaryBeanName); } // We have exactly one match. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); if (autowiredBeanNames != null) { autowiredBeanNames.add(entry.getKey()); } return entry.getValue(); } }
層層深刻,最終定位到DefaultListableBeanFactory.getBeanNamesForType(...)
根據方法名能夠看出,本方法能夠根據類型找到對應的類名,換句話說就是:根據咱們例子中提供的接口ITestService能夠找到其實現類TestServiceImpl。
未完待續....