Spring 源碼學習過程:html
1、搞明白IOC能作什麼,是怎麼作的
1. 搞明白IOC能作什麼?
IOC是用爲用戶建立、管理實例對象的。用戶須要實例對象時只須要向IOC容器獲取就好了,不用本身去建立,從而達到與具體類解耦。 java
2. IOC是怎麼作到的,即它的實現步驟是怎麼樣的?
2.1 用戶配置bean定義
咱們使用Spring IOC時有幾種方式來配置bean定義呢?git
xml的方式:github
<bean id="abean" class="com.study.spring.samples.ABean"> <constructor-arg type="String" value="abean01"></constructor-arg> <constructor-arg ref="cbean"></constructor-arg> </bean> <bean id="cbean" class="com.study.spring.samples.CBean"> <constructor-arg type="String" value="cbean01"></constructor-arg> </bean>
註解方式:spring
package com.study.spring.samples; import com.study.spring.context.config.annotation.Autowired; import com.study.spring.context.config.annotation.Component; import com.study.spring.context.config.annotation.Qualifier; import com.study.spring.context.config.annotation.Value; @Component(initMethodName = "init", destroyMethodName = "destroy") public class ABean { private String name; private CBean cb; @Autowired private DBean dbean; @Autowired public ABean(@Value("leesmall") String name, @Qualifier("cbean01") CBean cb) { super(); this.name = name; this.cb = cb; System.out.println("調用了含有CBean參數的構造方法"); } public ABean(String name, CCBean cb) { super(); this.name = name; this.cb = cb; System.out.println("調用了含有CCBean參數的構造方法"); } public ABean(CBean cb) { super(); this.cb = cb; } }
Java-based容器配置方式:編程
@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
上面的AppConfig類等價於:設計模式
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
想了解java容器配置的朋友請看這篇文章:數據結構
2.2 IOC容器加載bean定義
用戶以上面的三種方式配置bean定義之後,Spring IOC容器怎麼來加載用戶的bean定義呢,這就須要咱們來告訴它了app
xml的方式告訴Spring IOC容器怎麼加載bean定義:
//類路徑下加載xml配置文件建立bean定義 ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); //文件系統下加載xml配置文件建立bean定義 ApplicationContext context1 = new FileSystemXmlApplicationContext("e:/study/application.xml"); //通用的xml方式加載xml配置文件建立bean定義 ApplicationContext context3 = new GenericXmlApplicationContext("file:e:/study/application.xml");
註解方式告訴Spring IOC容器怎麼加載bean定義:
xml方式指定註解要掃描的基礎包:
<beans> <context:component-scan base-package="com.study" /> </beans>
註解方式指定註解要掃描的基礎包:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages="com.study") public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
也可在代碼中經過API指定註解掃描的基礎包:
// 掃描註解的方式建立bean定義 ApplicationContext ctx= new AnnotationConfigApplicationContext(); ctx.scan("com.study"); ctx.refresh(); MyService myService = ctx.getBean(MyService.class);
Java-based容器配置告訴Spring IOC容器怎麼加載bean定義:
使用AnnotationConfigApplicationContext告訴Spring IOC容器怎麼加載bean定義配置
跟實例化一個ClassPathXmlApplicationContext時將Spring XML文件用做輸入類似,在實例化一個AnnotationConfigApplicationContext時可以使用@Configuration類做爲輸入。這就等等於Spring容器全然零XML配置:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
AnnotationConfigApplicationContext不侷限於僅僅使用@Configuration類。不論什麼@Component或JSR-330註解的類都可以做爲AnnotationConfigApplicationContext構造器的輸入:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
使用無參的構造器實例化AnnotationConfigApplicationContext,而後使用register()
方法對容器進行配置。這樣的方式在以編程方式構造一個AnnotationConfigApplicationContext時很是實用:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(AppConfig.class, OtherConfig.class); ctx.register(AdditionalConfig.class); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); myService.doStuff(); }
啓用scan(String…)
的組件掃描:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages="com.study") public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
scan方法掃描:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("com.study"); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); }
2、搞明白Spring IOC的從總體到部分
IOC總體是由以上幾部分組成起來工做的
3、找到Spring IOC入口,先理清楚主幹流程,而後再去研究各個流程的細節
咱們從上面的使用示例,很清楚地看到,咱們使用Spring IOC,只須要使用Spring提供的ApplicationContext這個API。ApplicationContext就是IOC容器。ApplicationContext就是Spring IOC的入口,源碼的學習就從它開始!
1. ApplicationContext是什麼
首先來了解ApplicationContext都是什麼,即它都有哪些角色、責任。它經過繼承不少接口而有不少角色。
ApplicationContext繼承的接口(角色)以下:
每一個角色擁有的職責(方法):
再來了解 ApplicationContext 本身中定義的方法:
2. Application的子實現
說明:
從 AbstarctApplicationContext 以後分爲兩類:xml 配置方式的實現和通用實現。它們的基本使用示例以下:
package com.study.leesmall.spring; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext; import com.study.leesmall.spring.service.Abean; import com.study.leesmall.spring.service.CombatService; //Spring不一樣的方式建立bean實例使用代碼示例 @Configuration public class TestApplication { public static void main(String[] args) { //類路徑下加載xml配置文件建立bean定義 ApplicationContext context1 = new ClassPathXmlApplicationContext("application.xml"); CombatService cs = context1.getBean(CombatService.class); cs.doInit(); cs.combating(); //文件系統下加載xml配置文件建立bean定義 ApplicationContext context2 = new FileSystemXmlApplicationContext("e:/study/application.xml"); cs = context2.getBean(CombatService.class); cs.doInit(); cs.combating(); //通用的xml方式加載xml配置文件建立bean定義 ApplicationContext context3 = new GenericXmlApplicationContext("file:e:/study/application.xml"); cs = context3.getBean(CombatService.class); cs.doInit(); cs.combating(); // 掃描註解的方式建立bean定義 ApplicationContext context4 = new AnnotationConfigApplicationContext(TestApplication.class); CombatService cs2 = context4.getBean(CombatService.class); cs2.combating(); //通用的方式加載xml配置文件或者掃描指定包下的類建立bean定義 System.out.println("------------------------------------------------------"); GenericApplicationContext context5 = new GenericApplicationContext(); new XmlBeanDefinitionReader(context5).loadBeanDefinitions("classpath:application.xml"); new ClassPathBeanDefinitionScanner(context5).scan("com.study.leesmall.spring.service"); // 必定要刷新 context5.refresh(); cs2 = context5.getBean(CombatService.class); cs2.combating(); Abean ab = context5.getBean(Abean.class); ab.doSomething(); } @Bean public CombatService getCombatService() { return new CombatService(120); } }
接下來,能夠打開每一個子去了解它們分別加入了什麼、實現了什麼?
1)ConfigurableApplicationContext 加入了什麼:
說明:
void addApplicationListener(ApplicationListener<?> listener):這個方法添加監聽 在這裏能夠進行發佈訂閱監聽的工做
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor):這個方法能夠對bean工廠進行獲取先後的AOP加強
void refresh() throws BeansException, IllegalStateException:這個方法是用來刷新IOC容器的,當往IOC容器裏面註冊了新的Bean定義時,調用這個方法去建立bean實例
2)AbstractApplicationContext裏面對前面的接口就開始有具體的實現了,好比addApplicationListener、addBeanFactoryPostProcessor、refresh等等
3)通用的實現GenericApplicationContext
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { private final DefaultListableBeanFactory beanFactory; @Nullable private ResourceLoader resourceLoader; private boolean customClassLoader = false; private final AtomicBoolean refreshed = new AtomicBoolean();
它實現了BeanDefinitionRegistry接口,該接口定義了bean定義信息的註冊行爲。即咱們能夠直接往GenericApplicationContext中註冊bean定義。
瞭解一下BeanDefinitionRegistry中定義的行爲:
都有誰實現了 BeanDefinitionRegistry 接口:
GenericApplicationContext中持有DefaultListableBeanFactory,GenericApplicationContext的bean定義註冊委託給了持有的DefaultListableBeanFactory
org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(String, BeanDefinition)對應代碼:
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
@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 existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] 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<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
接下來看GenericApplicationContext的兩個子AnnotationConfigApplicationContext和GenericXmlApplicationContext:
AnnotationConfigApplicationContext:
瞭解它的構造方法、register 方法
構造方法:
/** * Create a new AnnotationConfigApplicationContext that needs to be populated * through {@link #register} calls and then manually {@linkplain #refresh refreshed}. */ public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } /** * Create a new AnnotationConfigApplicationContext with the given DefaultListableBeanFactory. * @param beanFactory the DefaultListableBeanFactory instance to use for this context */ public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } /** * Create a new AnnotationConfigApplicationContext, deriving bean definitions * from the given annotated classes and automatically refreshing the context. * @param annotatedClasses one or more annotated classes, * e.g. {@link Configuration @Configuration} classes */ public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); refresh(); } /** * Create a new AnnotationConfigApplicationContext, scanning for bean definitions * in the given packages and automatically refreshing the context. * @param basePackages the packages to check for annotated classes */ public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }
從構造函數能夠看到要完成註解的掃描是經過註解bean定義讀取器AnnotatedBeanDefinitionReader和掃描器ClassPathBeanDefinitionScanner完成的
GenericXmlApplicationContext :
瞭解它的構造方法、load 方法
構造方法:
/** * Create a new GenericXmlApplicationContext that needs to be * {@link #load loaded} and then manually {@link #refresh refreshed}. */ public GenericXmlApplicationContext() { } /** * Create a new GenericXmlApplicationContext, loading bean definitions * from the given resources and automatically refreshing the context. * @param resources the resources to load from */ public GenericXmlApplicationContext(Resource... resources) { load(resources); refresh(); } /** * Create a new GenericXmlApplicationContext, loading bean definitions * from the given resource locations and automatically refreshing the context. * @param resourceLocations the resources to load from */ public GenericXmlApplicationContext(String... resourceLocations) { load(resourceLocations); refresh(); } /** * Create a new GenericXmlApplicationContext, loading bean definitions * from the given resource locations and automatically refreshing the context. * @param relativeClass class whose package will be used as a prefix when * loading each specified resource name * @param resourceNames relatively-qualified names of resources to load */ public GenericXmlApplicationContext(Class<?> relativeClass, String... resourceNames) { load(relativeClass, resourceNames); refresh(); }
從構造函數能夠看出GenericXmlApplicationContext 能從不一樣的輸入中加載bean定義
4、細節分析
從前面的分析,已經對Spring IOC的總體由哪些部分組成有一個瞭解了,下面就深刻去分析各個部分是怎麼實現的
1. Bean定義加載和解析
ApplicationContext如何加載、解析Bean定義的。讀源碼,咱們不光是瞭解一下這個過程,更重要的是看它是如何設計接口、類來配合解決這個問題的,以及有哪些擴展點、靈活之處。
1.1 xml文件的bean配置的加載和解析
xml配置方式的bean定義獲取過程:
首先,你本身必定要思考清楚這個。而後纔去看源碼,否則你都不知道它在幹嗎。
第一次來學習spring的源碼,該怎麼來看源碼呢?
一行一行看,理解每一行?這確定行不通。
對於未知的代碼,咱們並不清楚它的接口、類結構,方法設計。那怎麼看呢?
首先找準一個你熟悉的過程點,好比上面的過程當中,咱們比較熟悉xml解析,那xml解析的代碼你是能夠看懂的。
而後以debug的方式開啓發現之旅,從入口處開始一步一步往裏跟,直到看到找準的點的代碼。這時必定要記住這個點在哪一個類中。下次就能夠直接在這裏打斷點了。而後要把一路跟過來的調用棧截圖存下來。這個調用棧將是咱們重要的分析做者是如何設計接口、類的源泉。
第一次老是困難的,但以後就輕鬆了
開始吧,從這裏出發,斷點這行,調試運行
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
經過艱辛之旅,咱們獲得了調用棧:
在這裏咱們也能夠有捷徑得到這個調用棧,若是你夠細心,會聯想:
咱們知道IOC容器的工做過程是加載xml、解析xml獲得bean定義、把bean定義註冊到bean工廠裏面、bean工廠根據bean定義建立bean實例,最終目的是註冊bean定義到bean工廠建立bean實例,那麼咱們就根據ClassPathXmlApplicationContext的繼承體系先找到哪一個類裏面持有bean工廠,找到持有bean工廠的地方之後先看有沒有註冊bean定義相關的方法,根據繼承體系尋找,最終發如今父類AbstractRefreshableApplicationContext裏面持有Bean工廠DefaultListableBeanFactory:
/** Bean factory for this context. */ @Nullable private DefaultListableBeanFactory beanFactory;
經過在AbstractRefreshableApplicationContext裏面查找,沒有找到註冊bean定義相關的方法,那麼咱們就看DefaultListableBeanFactory的裏面有沒有註冊bean定義相關的方法,最終發現DefaultListableBeanFactory裏面果真有註冊bean定義的方法registerBeanDefinition
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
經過查看DefaultListableBeanFactory繼承體系,咱們能夠看到DefaultListableBeanFactory實現了BeanDefinitionRegistry這個接口來實現bean定義註冊
那麼咱們就在registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法裏面打個斷點,而後debug運行前面的示例代碼到這裏:
這樣咱們就能快速的拿到整個調用棧,而不用一步一步的去debug代碼了:
那麼怎麼來具體分析調用棧呢?
主要看調用棧(看從開始到註冊bean定義這件事情)用到了哪些類的哪些方法,看傳參。工做是如何分配、分工協做完成的。
看傳參重點是看輸入的參數(如xml配置文件的路徑)在這些類中是怎麼變化的(代碼的本質其實就是對輸入數據的各類處理獲得最終想要的結果),從而知道每個類是幹什麼用的。分析完整個調用棧之後,想要了解哪一部分就點擊對應的調用棧去分析就好了。
從調用棧能夠看到要加載xml、解析xml獲取bean定義、註冊bean定義到bean工廠這些事由如下4個部分組成:
說明:
方法裏面含有<init>的表示是在構造函數進行初始化,方法裏面帶有(AbstractApplicationContext).refresh()的表示調用的是AbstractApplicationContext父類的refresh()方法,其餘的相似
從方法和參數上咱們能夠看出這4部分分別是作什麼:
第一部分:初始化IOC容器,建立了內部的BeanFactory,而後將加載xml的工做委託給了XmlBeanDefinitionReader。
第二部分:XmlBeanDefinitionReader完成了對輸入數據:字符串——>Resource——>EncodedResource——>InputSource——>Document的轉變。
第三部分:DefaultBeanDefinitionDocumentReader完成對Document中元素的解析,得到Bean定義等信息。
第四部分:就是簡單的完成bean定義的註冊。
接下來,你就能夠針對每一部分去看你感興趣的處理邏輯、數據結構了。
好比:你可能對第三部分Document中元素的解析很感興趣,想搞清楚它是如何解析xml文檔中的各類標籤元素的。
1.1.1 xml元素的解析
從第三部分的調用棧上,咱們能夠看到以下變化:
1——>2 :Document ——>Element
看 doRegisterBeanDefinitions 方法:
接下來看在 parseBeanDefinitions 方法中是如何來處理裏面<beans>的元素的:
至於它是如何解析bean的不重要,很簡單的。咱們重點關心的是它是如何處理其餘名稱空間元素的,由於這裏是個變化點:其餘模塊所須要的標籤各異,表示的信息也不一樣,它也不知道其餘模塊會有哪些標籤。
它是如何作到以不變應萬變的?看下面的xml配置示例:
就來看 parseCustomElement方法:
先來看一下 NamespaceHandler 的繼承體系:
NamespaceHandler 裏面定義的方法:
請詳細看NamespaceHandler的接口註釋,方法註釋說明了方法的用法。
問題:名稱空間對應的處理器在不一樣的模塊實現,這裏是如何加載到的?如事務處理的根本就不在如今的這裏。那就要去看圖中的這條語句的方法調用了:
進入resolve 方法
它是一個接口,那這裏用的是它的什麼實現類對象呢?
咱們看到有兩個屬性classLoader,handlerMappingsLocation。從handlerMappingsLocation這個名字能知道這是處理器與名稱空間的映射的配置所在的地址。從前兩個構造方法,咱們看到它給入了一個常量的地址值:
可大膽推測它是要到類目錄下去找這個文件。看下咱們的 spring 的模塊 jar 包下有沒有這個文件
接下來來看下 resolve 方法:
請把NamespaceHandler、NamespaceHandlerResolver的類圖畫出來。思考一下這裏有用到什麼設計原則、設計模式?【很重要】
擴展:
1)若是你須要加一個本身開發的模塊(含自定義的bean定義標籤)到spring中,你是否能夠作到了。
請看個人文章:
中的dubbo框架是如何跟spring進行整合的
2)spring標籤處理這裏的設計:模塊之間能夠靈活組合,配置在各自的模塊中,即插即用。你是否能夠把它應用到你的系統設計上。
策略模式跟工廠模式的組合使用
那麼通用的實現 GenericXmlApplicationContext 加載xml、獲取bean定義、註冊bean定義的調用棧是否也是同樣的呢?
ClassPathXmlApplicationContext加載xml、獲取bean定義、註冊bean定義:
//類路徑下加載xml配置文件建立bean定義 ApplicationContext context1 = new ClassPathXmlApplicationContext("application.xml");
斷點在DefaultListableBeanFactory中的註冊方法上:
GenericXmlApplicationContext 加載xml、獲取bean定義、註冊bean定義:
//通用的xml方式加載xml配置文件建立bean定義 ApplicationContext context3 = new GenericXmlApplicationContext("file:e:/study/application.xml");
斷點仍是在DefaultListableBeanFactory中的註冊方法上:
通過對比:
ClassPathXmlApplicationContext和GenericXmlApplicationContext 加載xml、獲取bean定義、註冊bean定義的過程是同樣的
1.2 bean工廠DefaultListableBeanFactory
通過前面的分析,咱們發現bean定義都是註冊到DefaultListableBeanFactory中。接下來就來認識一下它
在ApplicationContext的兩類實現中,經過查看繼承體系咱們均可以看到ApplicationContext中持有DefaultListableBeanFactory:
xml配置方式的實現的父類:
通用實現:
下面咱們來看一下ApplicationContext和BeaFatory的關係
1.3 註解方式的bean配置的加載和解析
入口:
// 掃描註解的方式建立bean定義 AnnotationConfigApplicationContext context4 = new AnnotationConfigApplicationContext("com.study.leesmall.spring.service");
斷點仍是在DefaultListableBeanFactory中的註冊方法上:
debug到上面的斷點之後拿到的調用棧:
在這個調用棧中,咱們併發沒有看到它作包掃描的相關工做。從下往上看這個執行棧,點第2個執行棧看代碼:
它如今是在作一些初始化的準備處理,從這裏咱們獲知,它作了registerAnnotationConfigProcessors。從名字上理解就是註冊了一些註解配置的處理器。究竟是一些什麼processors,點方法進去看看:
註冊各類processor
它註冊了不少的processor,都是些什麼Processor?點第一個的類名進去看看
BeanDefinitionRegistryPostProcessor擴展了BeanFactoryPostProcessor,增長了BeanDefinitionRegistry位置的處理,即它能夠提早對註冊好的BeanDefinitionRegistry進行前置處理。
下面咱們來看一下BeanFactoryPostProcessor:
來看看BeanFactoryPostProcessor有哪些實現:
這是springIOC中給咱們提供的又一個【擴展點】,讓咱們能夠在beanFactory開始建立Bean實例前對beanFactory進行一些處理!!!
使用示例以下:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations" value="classpath:application.properties"/> </bean>
擴展一個本身的BeanFactoryPostProcessor:
package com.study.leesamll.spring.ext; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.stereotype.Component; @Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println(this + " 擴展一個本身的BeanFactoryPostProcessor"); } }
入口:
// 掃描註解的方式建立bean定義 AnnotationConfigApplicationContext context4 = new AnnotationConfigApplicationContext("com.study.mike.spring.service"); context4.close();
BeanFactoryPostProcessor的類圖
1.3.1 掃描包獲取bean定義的過程是怎樣的?
前面咱們分析到,註解方式是在scan方法開始進行掃描的
那麼咱們就在這個方法的這裏再打個斷點,記住以前的bean工廠註冊方法裏面的斷點保留,debug調試看一下調用棧
從調用棧上咱們可看到有哪些類參與進來,在哪裏發生的什麼。
下面來看一下是怎麼掃描的:
下面來看一下掃描候選組件的方法:
類圖以下:
參考文章:
完整代碼獲取地址:https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-source-study