spring里IOC的原理就不詳細寫了, 如果想要搞清楚自動掃描組件是如何實現的,還有@Resouce @PostConstruct等注解的工作原理,最好可以先搞清楚整個IOC容器的運作原理再來分析這個過程.
IOC容器里的bean的生命周期如下:
1. beanDefinition 的解析
2. beanDefinition 的注冊
3. bean的實例化
4. bean 的依賴注入
5. bean的初始化 (在初始化前后分別有對BeanPostProcessor的應用)
當然,中整個過程中還有很多細節的部分, 比如循環依賴的解決, 是否惰性注入, 等等, 還少不了很多地方都有BeanPostProcessor的埋點調用.
以上都是對IOC的簡單背景介紹, 下面開始說正文.
在解析xml的時候, 如果發現了context:component-scan 這個標簽, 在解析xml的過程中,就會找到這個標簽的parsre類, 也就是ComponentScanBeanDefinitionParser, 具體怎么找到這個解析器的, 請大家自行回顧下beanDefinition 的解析過程.
然后我們看下ComponentScanBeanDefinitionParser 里的parse方法, 一直追下去, 會看到把包下各個類當作resource返回, 然后使用ASM對類信息進行解析. 大家自己可以看下關於ASM的內容 , 在看源碼的時候,會更容易理解. 可以看到在對類信息進行解析的時候, 其實只是記錄了類級別的信息, 比較類名, 類上的注解 等等, 在解析到類內的方法和域的時候, 使用的都是一個空的visitor, 也就意味着, 在解析的過程中, 並沒有對方法和域進行任何實質性的解析,也沒有記錄任何關於方法和域的信息. 然后就拿着類級別的信息, 生成了對應的beanDefinition, 然后將這些beanDefinition注冊到容器里. 看到這里, 我本來是很困惑的, 沒有對域和方法進行解析, 那這個類內部的依賴是怎么注入的呢?
下面開始解釋@Resouce @PostConstruct 這類注解的生效原理
首先看@Resouce這個注解是怎么生效的. 我們可以看下CommonAnnotationBeanPostProcessor 這個類, 在postProcessPropertyValues 方法里往下追, 會看到對@Resouce 這個注解的識別, 還有相應的域信息的記錄, 然后還有相應bean的獲取, 還有進行注入等操作. 但是postProcessPropertyValues 這個方法是什么時候被調用的呢? 再分析一下調用鏈, 發現是在bean的依賴注入的過程中, 掉用了所有的InstantiationAwareBeanPostProcessor 的postProcessPropertyValues 方法, 來對使用了@Resouce注解的類進行依賴注入. 總結一下, 在整個bean的生命周期里, 都沒有保持關於@Resouce這個注解的任何信息, 它的生效, 完全是同過一類BeanPostProcessor來實現. 這也是為什么在解析beanDefinition的時候, 沒有解析任何關於類內部域和方法的原因
下面再說@PostConstruct 這個注解是怎么生效的. 原理是一樣的, 這次我們看下InitDestroyAnnotationBeanPostProcessor 這個類, 內部的postProcessBeforeInitialization 方法, 可以看到, 在這個方法里 , 首先對@PostConstruct 這個注解進行識別, 然后通過反射, 對這個方法進行了調用. 而postProcessBeforeInitialization 這個方法的被調用的地方就是上面在bean的生命周期里提到的 5. bean的初始化 (在初始化前后分別有對BeanPostProcessor的應用)
好, 這個時候, 其實可以看到IOC的設計思路了, 很多的注解的功能的生效, 並不是在解析beanDefinition的時候, 先對這些注解的信息進行解析, 保留, 然后到對應的點直接調用. 而是在對應的點, 通過不同的BeanPostProcessor開始對類內部進行解析, 如果解析到了感興趣的注解或別的元素的話, 直接開始調用.
那么我們還剩下最后一個問題, 這些BeanPostProcessor 是什么地方注冊進去的呢? 我們回到ComponentScanBeanDefinitionParser 這個類里, 看下parse 這個方法里調用了registerComponents 這個方法,往下追, 會看到AnnotationConfigUtils.registerAnnotationConfigProcessors 的調用,在這里注冊了很多的BeanPostProcessor, 其中就有CommonAnnotationBeanPostProcessor, 還有別的一些注解, 包括一些會@Autowired等注解處理的BeanPostProcessor. 這也就是為什么說 <context:component-scan base-package=”XX.XX”/> 這個配置其實包含了類如<context:annotation-config/> 這些配置的原因.....
好, 但是還有一點不對勁, 這里的對BeanPostProcessor的注冊, 只是把這寫BeanPostProcessor當作普通的beanDefinition向容器進行了注冊, 這寫beanDefinition是什么時候生成真正的bean實例, 並被注冊成為容器的BeanPostProcessor的呢? 這時就需要看下AbstractApplicationContext 類里的refresh方法了. 在這里完成了很多的操作, 包括對容器的初始化, beandefinition的注冊等等一些操作. 在這個方法里可以看到對registerBeanPostProcessors的調用, 這這里, 會找到已經注冊了的beandefinition里的BeanPostProcessor, 然后對它們進行實例話, 然后把bean實例注冊為容器真正的BeanPostProcessor
也就是說, 如果我們想要把自己寫的BeanPostProcessor 在spring內生效的話, 只要繼承BeanPostProcessor 這個接口, 然后把這個類向spring注冊就好了
好了, 以上, 就是對自動掃描相關的解說, 希望對大家有幫助
轉載請注明出處: http://www.cnblogs.com/zhaoxinshanwei/p/8399176.html
