javascript
深入理解 Spring 之源码剖析IOC
推薦兩篇文章:
https://www.jianshu.com/p/e4ca039a2272
https://www.cnblogs.com/ITtangtang/p/3978349.html
一、介紹
作為Java程序員,Spirng我們再熟悉不過,可以說比自己的女朋友還要親密,每天都會和他在一起,然而我們真的了解spring嗎?
我們都知道,Spring的核心是IOC和AOP,但樓主認為,如果從這兩個核心中挑選一個更重要的,那非IOC莫屬。AOP 也是依賴于IOC,從某些角度講,AOP就是IOC的一個擴展功能。
什么是IOC? IOC解決了什么問題?IOC的原理是什么?Spring的IOC是怎么實現的?今天我們將會將這幾個問題一起解決。
1. 什么是IOC?
控制反轉(Inversion of Control,縮寫為IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體,將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。
2. IOC 解決了什么問題?
簡單來說, IOC 解決了類與類之間的依賴關系。程序員將控制類與類之間依賴的權利交給了IOC,即:控制被反轉了。
3. IOC 的原理是什么?
其實 IOC 的原理很簡單,底層就是java的反射。給定一個字符串能創建一個實例,利用set方法對實例的依賴進行注入。
4. Spring的IOC是怎么實現的?
在開始研究源碼之前,樓主有必要介紹一下IOC的一些核心組件,否則一旦進入源碼,就會被細節捆住,無法從宏觀的角度理解IOC。
BeanFactory:這是IOC容器的接口定義,如果將IOC容器定位為一個水桶,那么BeanFactory 就定義了水桶的基本功能,能裝水,有把手。這是最基本的,他的實現類可以拓展水桶的功
ApplicationContext:這是我們最常見的,上面我們說水桶,BeanFactory是最基本的水桶,而 ApplicationContext 則是擴展后的水桶,它通過繼承MessageSource,ResourceLoader,ApplicationEventPublisher 接口,在BeanFactory 簡單IOC容器的基礎上添加了許多對高級容器的支持。
BeanDefinition:我們知道,每個bean都有自己的信息,各個屬性,類名,類型,是否單例,這些都是bena的信息,spring中如何管理bean的信息呢?對,就是 BeanDefinition, Spring通過定義 BeanDefinition 來管理基于Spring的應用中的各種對象以及他們直接的相互依賴關系。BeanDefinition 抽象了我們對 Bean的定義,是讓容器起作用的主要數據類型。對 IOC 容器來說,BeanDefinition 就是對依賴反轉模式中管理的對象依賴關系的數據抽象。也是容器實現依賴反轉功能的核心數據結構
二、源碼分析
1. 搭建源碼研究環境
Maven下載即可
2. 開啟研究源碼第一步
public static void main(String[] args) throws ClassNotFoundException {ApplicationContext ctx = new FileSystemXmlApplicationContext ("META-INF/spring/orders-provider.xml");System.out.println("number : " + ctx.getBeanDefinitionCount());RedisUtil ru = (RedisUtil) ctx.getBean("redisUtil");System.out.println(ru); }熟悉的 ApplicatContext ,看名字是應用上下文,什么意思呢?就是spirng整個運行環境的背景,好比一場舞臺劇,ApplicatContext 就是舞臺,IOC 管理的Bean 就是演員,Core 就是道具。而ApplicatContext 的標準實現是 FileSystemXmlApplicationContext。
該類的構造方法中包含了容器的啟動,IOC的初始化。所以我們 debug 啟動該項目,運行main方法。打好斷點。
3. 從 FileSystemXmlApplicationContext 開始剖析
從這里開始,我們即將進入復雜的源碼。
我們進入 FileSystemXmlApplicationContext 的構造方法:
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {this(new String[] {configLocation}, true, null); }public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {this(configLocations, true, null); }可以看到該構造方法被重載了,可以傳遞 configLocation 數組,也就是說,可以傳遞過個配置文件的地址。默認刷新為true,parent 容器為null。進入另一個構造器:
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();} }該構造器做了2件事情,一是設置配置文件,二是刷新容器,我們可以感覺到,refresh 方法才是初始化容器的重要方法。我們進入該方法看看:該方法是 FileSystemXmlApplicationContext 的父類 AbstractApplicationContext 的方法。
4. AbstractApplicationContext.refresh() 方法實現
/*** 1. 構建Be按Factory,以便產生所需要的bean定義實例* 2. 注冊可能感興趣的事件* 3. 創建bean 實例對象* 4. 觸發被監聽的事件*/ @Override public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 為刷新準備應用上下文 prepareRefresh();// 告訴子類刷新內部bean工廠,即在子類中啟動refreshBeanFactory()的地方----創建bean工廠,根據配置文件生成bean定義ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 在這個上下文中使用bean工廠 prepareBeanFactory(beanFactory);try {// 設置BeanFactory的后置處理器 postProcessBeanFactory(beanFactory);// 調用BeanFactory的后處理器,這些后處理器是在Bean定義中向容器注冊的 invokeBeanFactoryPostProcessors(beanFactory);// 注冊Bean的后處理器,在Bean創建過程中調用 registerBeanPostProcessors(beanFactory);// 對上下文的消息源進行初始化 initMessageSource();// 初始化上下文中的事件機制 initApplicationEventMulticaster();// 初始化其他的特殊Bean onRefresh();// 檢查監聽Bean并且將這些Bean向容器注冊 registerListeners();// 實例化所有的(non-lazy-init)單件 finishBeanFactoryInitialization(beanFactory);// 發布容器事件,結束refresh過程 finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// 為防止bean資源占用,在異常處理中,銷毀已經在前面過程中生成的單件bean destroyBeans();// 重置“active”標志 cancelRefresh(ex);throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore... resetCommonCaches();}} }可以說該方法就是整個IOC容器初始化的所有邏輯。因此,如果讀懂了該方法的每一行代碼,就了解了spring的整個功能。該方法的調用層次之深可以想象一下。
我們先大致說下該方法的步驟:
我們一個個來看:
首先構建BeanFactory,在哪里實現的呢?也就是obtainFreshBeanFactory 方法,返回了一個ConfigurableListableBeanFactory,該方法調用了 refreshBeanFactory() ,該方法是個模板方法,交給了 AbstractRefreshableApplicationContext 去實現。我們看看該方法實現:
@Override protected final void refreshBeanFactory() throws BeansException {if (hasBeanFactory()) {// 如果存在就銷毀 destroyBeans();closeBeanFactory();}try {// new DefaultListableBeanFactory(getInternalParentBeanFactory())DefaultListableBeanFactory beanFactory = createBeanFactory();// 設置序列化 beanFactory.setSerializationId(getId());// 定制的BeanFactory customizeBeanFactory(beanFactory);// 使用BeanFactory加載bean定義 AbstractXmlApplicationContext loadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);} }可以看到BeanFactory的創建過程,首先判斷是否存在了 BeanFactory,如果有則銷毀重新創建,調用 createBeanFactory 方法,該方法中就是像注釋寫的:創建了 DefaultListableBeanFactory ,也既是說,DefaultListableBeanFactory 就是 BeanFactory的默認實現。然后我們看到一個很感興趣的方法,就是 loadBeanDefinitions(beanFactory),看名字是加載 Definitions,這個我們很感興趣,我們之前說過, Definition 是核心之一,代表著 IOC 中的基本數據結構。該方法也是個抽象方法,默認實現是 AbstractXmlApplicationContext ,我們看看該方法實現:
@Override 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); }該方法我沒有寫中文注釋,我們看看英文注釋: 首先創建一個 XmlBeanDefinitionReader ,用于讀取XML中配置,設置了環境,資源加載器,最后初始化,加載。可以說,該方法將加載,解析Bean的定義,也就是把用戶定義的數據結構轉化為 IOC容器中的特定數據結構。而我們關心的則是最后一行的 loadBeanDefinitions(beanDefinitionReader) 方法。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();if (configResources != null) {reader.loadBeanDefinitions(configResources);}String[] configLocations = getConfigLocations();if (configLocations != null) { // 加載給定的路徑文件 reader.loadBeanDefinitions(configLocations);} }該方法會略過第一個if塊,進入第二個if塊,進入 AbstractBeanDefinitionReader.loadBeanDefinitions(String... locations) 方法,該方法內部循環加載配置文件:
@Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {Assert.notNull(locations, "Location array must not be null");int counter = 0;for (String location : locations) {counter += loadBeanDefinitions(location);}return counter; }我們關心的是 for 循環中的loadBeanDefinitions(location)方法,該方法核心邏輯在 AbstractBeanDefinitionReader.loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) 方法中:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {ResourceLoader resourceLoader = getResourceLoader();if (resourceLoader == null) {throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");}if (resourceLoader instanceof ResourcePatternResolver) {// Resource pattern matching available.try {Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);int loadCount = loadBeanDefinitions(resources); // 根據配置文件加載bean定義if (actualResources != null) {for (Resource resource : resources) {actualResources.add(resource);}}if (logger.isDebugEnabled()) {logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");}return loadCount;}catch (IOException ex) {throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);}}else {// Can only load single resources by absolute URL.Resource resource = resourceLoader.getResource(location);int loadCount = loadBeanDefinitions(resource);if (actualResources != null) {actualResources.add(resource);}if (logger.isDebugEnabled()) {logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");}return loadCount;} }該方法首先獲取資源加載器,然后進入 if 塊,獲取資源數組,調用 loadBeanDefinitions(resources) ,根據配置文件加載Bean定義。進入該方法后,循環加載resource 資源數組,進入 loadBeanDefinitions(resource) 方法中,最后進入到 XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource) 方法中,該方法主要調用 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 方法。我們有必要看看該方法實現:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {try {Document doc = doLoadDocument(inputSource, resource);return registerBeanDefinitions(doc, resource); // 真正的注冊bean }catch (BeanDefinitionStoreException ex) {throw ex;}catch (SAXParseException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);}catch (SAXException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"XML document from " + resource + " is invalid", ex);}catch (ParserConfigurationException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from " + resource, ex);}catch (IOException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from " + resource, ex);}catch (Throwable ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from " + resource, ex);} }可以看出該方法主要邏輯是根據輸入流加載 Document 文檔對象,然后根據得到的文檔對象注冊到容器,因此我們看看倒是是如何注冊到容器的,該方法首先創建一個 BeanDefinitionDocumentReader, 用于讀取 BeanDefinition,該對象會調用 registerBeanDefinitions(doc, createReaderContext(resource)) 方法,該方法最后從文檔對象總獲取根元素,最后調用DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(root) 進行注冊。該方法最核心的邏輯就是調用 parseBeanDefinitions(root, this.delegate),我們看看該方法具體實現:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {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;if (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate); // 解析 }else {delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);} }該方法就是一個解析XML 文檔的步驟,核心是調用 parseDefaultElement(ele, delegate),我們進入該方法查看,該方法調用了 processBeanDefinition(ele, delegate) 方法進行解析。我們有必要看看該方法:
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));} }首先創建一個 BeanDefinitionHolder,該方法會調用 BeanDefinitionReaderUtils.registerBeanDefinition 方法, 最后執行容器通知事件。該靜態方法實現如下:
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.String beanName = definitionHolder.getBeanName();registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {registry.registerAlias(beanName, alias);}} }可以看到首先從bean的持有者那里獲取了beanName,然后調用 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()), 將bena的名字和 BeanDefinition 注冊,我們看看最后的邏輯:
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition oldBeanDefinition;oldBeanDefinition = this.beanDefinitionMap.get(beanName);if (oldBeanDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +"': There is already [" + oldBeanDefinition + "] bound.");}else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (this.logger.isWarnEnabled()) {this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +oldBeanDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(oldBeanDefinition)) {if (this.logger.isInfoEnabled()) {this.logger.info("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + oldBeanDefinition +"] with [" + beanDefinition + "]");}}else {if (this.logger.isDebugEnabled()) {this.logger.debug("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + oldBeanDefinition +"] with [" + beanDefinition + "]");}}this.beanDefinitionMap.put(beanName, beanDefinition);}else {if (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;if (this.manualSingletonNames.contains(beanName)) {Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);updatedSingletons.remove(beanName);this.manualSingletonNames = updatedSingletons;}}}else {// Still in startup registration phase // 最終放進這個map 實現注冊this.beanDefinitionMap.put(beanName, beanDefinition);// 走這里this.beanDefinitionNames.add(beanName);this.manualSingletonNames.remove(beanName);}this.frozenBeanDefinitionNames = null;}if (oldBeanDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);} }該方法可以說是注冊bean的最后一步,將beanName和 beanDefinition 放進一個 ConcurrentHashMap(256) 中。
那么這個 beanDefinition 是時候創建的呢? 就是在 DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法中,在這里創建了 BeanDefinitionHolder, 而該實例中解析Bean并將Bean 保存在該對象中。所以稱為持有者。該實例會調用 parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) 方法,該方法用于解析XML文件并創建一個 BeanDefinitionHolder 返回,該方法會調用 parseBeanDefinitionElement(ele, beanName, containingBean) 方法, 我們看看該方法:
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {this.parseState.push(new BeanEntry(beanName));String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); // 類全限定名稱 }try {String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 創建 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));parseMetaElements(ele, bd);parseLookupOverrideSubElements(ele, bd.getMethodOverrides());parseReplacedMethodSubElements(ele, bd.getMethodOverrides());parseConstructorArgElements(ele, bd);parsePropertyElements(ele, bd);parseQualifierElements(ele, bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));return bd;}catch (ClassNotFoundException ex) {error("Bean class [" + className + "] not found", ele, ex);}catch (NoClassDefFoundError err) {error("Class that bean class [" + className + "] depends on not found", ele, err);}catch (Throwable ex) {error("Unexpected failure during bean definition parsing", ele, ex);}finally {this.parseState.pop();}return null; }我們看看該方法,可以看到,該方法從XML元素中取出 class 元素,然后拿著className調用 createBeanDefinition(className, parent) 方法,該方法核心是調用 BeanDefinitionReaderUtils.createBeanDefinition 方法,我們看看該方法:
public static AbstractBeanDefinition createBeanDefinition(String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {GenericBeanDefinition bd = new GenericBeanDefinition(); // 泛型的bean定義,也就是最終生成的bean定義。 bd.setParentName(parentName);if (className != null) {if (classLoader != null) {bd.setBeanClass(ClassUtils.forName(className, classLoader)); // 設置Class }else {bd.setBeanClassName(className);}}return bd; }該方法很簡單,創建一個 Definition 的持有者,然后設置該持有者的Class對象,該對象就是我們在配置文件中配置的Class對象。最后返回。
到這里,我們一走完了第一步,創建bean工廠,生成Bean定義。但還沒有實例化該類。
5. 如何創建Bean實例并構建Bean的依賴關系網
我們剛剛創建了Bean工廠,并創建 BeanDefinitions 放進Map里,以beanName為key。那么我們現在有了Bean定義,但還沒有實例,也沒有構建Bean與Bean之間的依賴關系。我們知道,構建依賴關系是 IOC 的一個重要的任務,我們怎么能放過。那么是在哪里做的呢?在 finishBeanFactoryInitialization(beanFactory) 方法中。該方法中重要的一步是 : beanFactory.preInstantiateSingletons(),我們有必要看看該方法實現:
public void preInstantiateSingletons() throws BeansException {if (this.logger.isDebugEnabled()) {this.logger.debug("Pre-instantiating singletons in " + this);}// 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.List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);// Trigger initialization of all non-lazy singleton beans...for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); // 注意:FactoryBeanboolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {@Overridepublic Boolean run() {return ((SmartFactoryBean<?>) factory).isEagerInit();}}, getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}else {getBean(beanName); // 創建bean }}}// 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>() {@Overridepublic Object run() {smartSingleton.afterSingletonsInstantiated();return null;}}, getAccessControlContext());}else {smartSingleton.afterSingletonsInstantiated();}}} }該方法首先循環所有的BeanNames,并且調用getBean方法,該方法實際上就是創建bean并遞歸構建依賴關系。該方法會調用 doGetBean(name, null, null, false),我們進入該方法查看,該方法很長,樓主挑選重要代碼:
String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);getBean(dep); // 遞歸 } }// Create bean instance. if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic 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); }可以看到,該方法首先會獲取依賴關系,拿著依賴的BeanName 遞歸調用 getBean方法,直到調用 getSingleton 方法返回依賴bean,而 getSingleton 方法的參數是 createBean 返回的實例,該方法內部調用 AbstractAutowireCapableBeanFactory.doCreateBean 方法:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)throws BeanCreationException {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);mbd.resolvedTargetType = beanType;// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});}// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {exposedObject = initializeBean(beanName, exposedObject, mbd);}}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject; }該方法很長,我們只關注二行代碼:
我們看看 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); }該方法的doc注釋是這樣介紹的:為指定的bean創建一個新的實例,使用適當的實例化策略:工廠方法、構造函數自動裝配或簡單實例化。我們看,該方法首先創建Class 對象,然后獲取構造器對象,最后調用 instantiateBean(beanName, mbd) 方法,我們看看該方法實現:
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>() {@Overridepublic 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);} }該方法核心邏輯是 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent),攜帶BeanName ,RootBeanDefinition ,發揮的策略對象是 SimpleInstantiationStrategy,該方法內部調用靜態方法 BeanUtils.instantiateClass(constructorToUse), 組后調用 Constructor 的 newInstance 方法, 也就是最終使用反射創建了該實例:
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, "Is it an abstract class?", ex);}catch (IllegalAccessException ex) {throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);}catch (IllegalArgumentException ex) {throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);}catch (InvocationTargetException ex) {throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());} }該方法會判斷是否是 Kotlin 類型。如果不是,則調用構造器的實例方法。
到這里,我們的實例已經創建。但是我們的實例的依賴還沒有設置,剛剛我們在 doCreateBean 方法說關心2行代碼:
我們已經解析了第一個,現在看第二個方法:
我們看看該方法:
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {PropertyValues pvs = mbd.getPropertyValues();if (bw == null) {if (!pvs.isEmpty()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// Skip property population phase for null instance.return;}}// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.boolean continueWithPropertyPopulation = true;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {continueWithPropertyPopulation = false;break;}}}}if (!continueWithPropertyPopulation) {return;}if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);if (hasInstAwareBpps || needsDepCheck) {PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);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); }該方法核心邏輯是 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null), 即獲取該bean的所有屬性,也就是我們配置property元素。最后執行 applyPropertyValues(beanName, mbd, bw, pvs) 方法。注意,現在的PropertyValues 都是字符串,沒有值的,這個方法的作用就是獲取值,關鍵代碼:Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue),該方法會獲取 pvName 所對應的容器value,該方法內部會調用 BeanWrapperImpl.resolveReference(argName, ref) 方法,我們看看該方法:
private Object resolveReference(Object argName, RuntimeBeanReference ref) {try {String refName = ref.getBeanName();refName = String.valueOf(doEvaluate(refName));if (ref.isToParent()) {if (this.beanFactory.getParentBeanFactory() == null) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Can't resolve reference to bean '" + refName +"' in parent factory: no parent factory available");}return this.beanFactory.getParentBeanFactory().getBean(refName);}else {Object bean = this.beanFactory.getBean(refName);this.beanFactory.registerDependentBean(refName, this.beanName);return bean;}}catch (BeansException ex) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);} }其中有一行熟悉的代碼:bean = this.beanFactory.getBean(refName),對,這里就是發生遞歸的地方。該方法會拿著屬性名稱從容器中獲取實例。
我們回到 applyPropertyValues 方法。此時deepCopy 集合已經有值了,不再僅僅是字符串了。然后調用 setPropertyValues(new MutablePropertyValues(deepCopy)) 方法, 該方法會調用 AbstractPropertyAccessor.setPropertyValues 方法完成注入,而該方法會循環元素列表, 循環中調用 setPropertyValue(PropertyValue pv) 方法, 該方法最后會調用 nestedPa.setPropertyValue(tokens, pv) 方法, 該方法又會調用 processLocalProperty(tokens, pv) 方法,該方法最后又會調用 ph.setValue(valueToApply) 方法,也就是BeanWrapperImpl.setValue() 方法,終于,我們要看到反射了,看到反射說明到了盡頭。
@Override public void setValue(final Object object, Object valueToApply) throws Exception {final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :this.pd.getWriteMethod());if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {if (System.getSecurityManager() != null) {AccessController.doPrivileged(new PrivilegedAction<Object>() {@Overridepublic Object run() {writeMethod.setAccessible(true);return null;}});}else {writeMethod.setAccessible(true);}}final Object value = valueToApply;if (System.getSecurityManager() != null) {try {AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {@Overridepublic Object run() throws Exception {writeMethod.invoke(object, value);return null;}}, acc);}catch (PrivilegedActionException ex) {throw ex.getException();}}else {writeMethod.invoke(getWrappedInstance(), value);} }該方法是最后一步,我們看到該方法會找的set方法,然后調用 Method 的 invoke 方法,完成屬性注入。
真的不容易。
三、總結
我們從源碼層面剖析 IOC 的初始化過程,也了解了 IOC 的底層原理實現, 我們總結一下: Spring 的 Bean 其實就是 BeanDefinition, 在 Bean 的創建和依賴注入的過程中, 需要根據 BeanDefinition 的信息來遞歸的完成依賴注入, 從我們分析的代碼可以看到,這些遞歸都是以 getBean() 為入口的, 一個遞歸是在上下文體系中查找需要的 Bean 和創建 Bean 的遞歸調用, 另一個 Bean 實在依賴注入時,通過遞歸調用容器的 getBean 方法, 得到當前的依賴 Bean, 同時也觸發對依賴 Bean 的創建和注入. 在對 Bean 的屬性盡心依賴注入時, 解析的過程也是一個遞歸的過程, 這樣, 根據依賴關系, 一層一層的完成 Bean 的創建和注入, 知道最后完成當前 Bean 的創建, 有了這個頂層 Bean 的創建和對他的屬性依賴注入的完成, 意味著當前 Bean 相關的整個依賴鏈的注入也完成了.
總結一下 IOC 的初始化過程吧:
轉載于:https://www.cnblogs.com/yifanSJ/p/9232382.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的深入理解 Spring 之源码剖析IOC的全部內容,希望文章能夠幫你解決所遇到的問題。