'Java源碼框架分析Spring IoC源碼分析 基於註解'

Java XML 設計模式 Redis 文章 來一杯82年的Java 2019-08-23
"

為了感謝支持我的朋友!整理了一份Java高級架構資料、Spring源碼分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式等資料。私信回覆:555領取

一、 IoC 理論

IoC 全稱為 Inversion of Control,翻譯為 “控制反轉”,它還有一個別名為 DI(Dependency Injection),即依賴注入。

二、IoC方式

Spring為IoC提供了2種方式,一種是基於xml,另一種是基於註解。

  • 標籤來定義bean,進行管理。
  • @Bean註解來定義bean,進行管理。

本次文章我們就來分析下基於註解的IoC原理,在看文章之前我們可以帶一些疑問,這樣有助於我們更好的理解。

  1. @Bean是幹什麼用的?
  2. @Controller、@Service又是幹啥的?
  3. @CompoentScan註解是怎麼起作用的?
  4. Spring是怎麼發現@Bean、@Controller、@Service這些註解修飾的類的?
  5. 發現之後是怎麼註冊到IOC容器中的?
  6. IOC容器到底是個啥?

三、源碼分析

首先看下段代碼:

AnnotationConfigApplicationContext aac =
\t\t\t\tnew AnnotationConfigApplicationContext("com.mydemo");

AnnotationConfigApplicationContext可以實現基於Java的配置類(包括各種註解)加載Spring的應用上下文。避免使用application.xml進行配置。相比XML配置,更加便捷。

3.1、類結構圖

"

為了感謝支持我的朋友!整理了一份Java高級架構資料、Spring源碼分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式等資料。私信回覆:555領取

一、 IoC 理論

IoC 全稱為 Inversion of Control,翻譯為 “控制反轉”,它還有一個別名為 DI(Dependency Injection),即依賴注入。

二、IoC方式

Spring為IoC提供了2種方式,一種是基於xml,另一種是基於註解。

  • 標籤來定義bean,進行管理。
  • @Bean註解來定義bean,進行管理。

本次文章我們就來分析下基於註解的IoC原理,在看文章之前我們可以帶一些疑問,這樣有助於我們更好的理解。

  1. @Bean是幹什麼用的?
  2. @Controller、@Service又是幹啥的?
  3. @CompoentScan註解是怎麼起作用的?
  4. Spring是怎麼發現@Bean、@Controller、@Service這些註解修飾的類的?
  5. 發現之後是怎麼註冊到IOC容器中的?
  6. IOC容器到底是個啥?

三、源碼分析

首先看下段代碼:

AnnotationConfigApplicationContext aac =
\t\t\t\tnew AnnotationConfigApplicationContext("com.mydemo");

AnnotationConfigApplicationContext可以實現基於Java的配置類(包括各種註解)加載Spring的應用上下文。避免使用application.xml進行配置。相比XML配置,更加便捷。

3.1、類結構圖

Java源碼框架分析Spring IoC源碼分析 基於註解

主要類或接口說明:GenericApplicationContext——通用應用上下文,內部持有一個DefaultListableBeanFactory實例,這個類實現了BeanDefinitionRegistry接口,可以在它身上使用任意的bean definition讀取器。典型的使用案例是:通過BeanFactoryRegistry接口註冊bean definitions,然後調用refresh()方法來初始化那些帶有應用上下文語義(org.springframework.context.ApplicationContextAware)的bean,自動探測org.springframework.beans.factory.config.BeanFactoryPostProcessor等。

BeanDefinitionRegistry——用於持有像RootBeanDefinition和 ChildBeanDefinition實例的bean definitions的註冊表接口。DefaultListableBeanFactory實現了這個接口,因此可以通過相應的方法向beanFactory裡面註冊bean。GenericApplicationContext內置一個DefaultListableBeanFactory實例,它對這個接口的實現實際上是通過調用這個實例的相應方法實現的。

AbstractApplicationContext——ApplicationContext接口的抽象實現,沒有強制規定配置的存儲類型,僅僅實現了通用的上下文功能。這個實現用到了模板方法設計模式,需要具體的子類來實現其抽象方法。自動通過registerBeanPostProcessors()方法註冊BeanFactoryPostProcessor, BeanPostProcessor和ApplicationListener的實例用來探測bean factory裡的特殊bean——對比1分析

AnnotationConfigRegistry——註解配置註冊表。用於註解配置應用上下文的通用接口,擁有一個註冊配置類和掃描配置類的方法。

3.2 構造函數

 //默認構造函數,初始化一個空容器,容器不包含任何 Bean 信息,需要在稍後通過調用其register()
\t//方法註冊配置類,並調用refresh()方法刷新容器,觸發容器對註解Bean的載入、解析和註冊過程
\tpublic AnnotationConfigApplicationContext() {
\t\tthis.reader = new AnnotatedBeanDefinitionReader(this);
\t\tthis.scanner = new ClassPathBeanDefinitionScanner(this);
\t}
\t
\tpublic AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
\t\tsuper(beanFactory);
\t\tthis.reader = new AnnotatedBeanDefinitionReader(this);
\t\tthis.scanner = new ClassPathBeanDefinitionScanner(this);
\t}
\t
\t//最常用的構造函數,通過將涉及到的配置類傳遞給該構造函數,以實現將相應配置類中的Bean自動註冊到容器中
\tpublic AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
\t\t//調用無參構造函數,初始化AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner
\t\tthis();
\t\tregister(annotatedClasses);
\t\trefresh();
\t}
\t
\t//該構造函數會自動掃描以給定的包及其子包下的所有類,並自動識別所有的Spring Bean,將其註冊到容器中
\tpublic AnnotationConfigApplicationContext(String... basePackages) {
\t //初始化ClassPathBeanDefinitionScanner和AnnotatedBeanDefinitionReader
\t\tthis();//step1
\t\t//掃描包、註冊bean
\t\tscan(basePackages);//step2
\t refresh();//step3
\t}

主要屬性:

  • AnnotatedBeanDefinitionReader——BeanDefinition解析器用來解析帶註解的bean
  • ClassPathBeanDefinitionScanner——bean的掃描器 用來掃描類
  • 註冊解析傳入的配置類(使用類配置的方式進行解析)
  • 調用容器的refresh方法初始化容器

這裡我們用的是最後一種構造函數,即傳入一個包路徑。

3.3 IoC 之 構造函數初始化

首先看step1,調用了本類的無參構造函數:

public AnnotationConfigApplicationContext() {
\t\tthis.reader = new AnnotatedBeanDefinitionReader(this);
\t\tthis.scanner = new ClassPathBeanDefinitionScanner(this);
\t}

然後初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner

我們來看下ClassPathBeanDefinitionScanner的構造函數

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
\t\tthis(registry, true);
\t}

繼續跟蹤下去,最後調用的是這個方法:

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
\t\t\tEnvironment environment, @Nullable ResourceLoader resourceLoader) {
\t\tAssert.notNull(registry, "BeanDefinitionRegistry must not be null");
\t\t//為容器設置加載Bean定義的註冊器
\t\tthis.registry = registry;
\t\t//是否使用默認過濾規則
\t\tif (useDefaultFilters) {
\t\t\tregisterDefaultFilters();
\t\t}
\t\t//設置環境
\t\tsetEnvironment(environment);
\t\t//為容器設置資源加載器
\t\tsetResourceLoader(resourceLoader);
\t}

這裡面最主要的是registerDefaultFilters()方法,初始化spring掃描默認過濾規則,對應@ComponentScan註解,如果沒有自定義規則,就初始化默認過濾規則。

這裡調用的是ClassPathScanningCandidateComponentProvider類中的registerDefaultFilters()方法:

//向容器註冊過濾規則
@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
\t//向要包含的過濾規則中添加@Component註解類
\t//@Service和@Controller都是Component,因為這些註解都添加了@Component註解
\tthis.includeFilters.add(new AnnotationTypeFilter(Component.class));
\t//獲取當前類的類加載器
\tClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
\ttry {
\t\t//向要包含的過濾規則添加JavaEE6的@ManagedBean註解
\t\tthis.includeFilters.add(new AnnotationTypeFilter(
\t\t\t\t((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
\t\tlogger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
\t}
\tcatch (ClassNotFoundException ex) {
\t\t// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
\t}
\ttry {
\t\t//向要包含的過濾規則添加@Named註解
\t\tthis.includeFilters.add(new AnnotationTypeFilter(
\t\t\t\t((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
\t\tlogger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
\t}
\tcatch (ClassNotFoundException ex) {
\t\t// JSR-330 API not available - simply skip.
\t}
}

這裡面有兩個關鍵變量:

  • private final List includeFilters = new LinkedList<>();
  • private final List excludeFilters = new LinkedList<>();

includeFilters表示要包含的註解,即只有包含includeFilters中的註解,才會被掃描

excludeFilters表示要排除的註解,即包含excludeFilters中的註解不會被掃描

在這個方法中,includeFilters集合中添加了@Component、JavaEE6的@ManagedBean和JSR-330的@Named註解

而excludeFilters集合沒做任何變動,即沒有要排除的註解

總結:

所以默認規則就是,只要包含了@Component、JavaEE6的@ManagedBean和JSR-330的@Named這3個註解中的任意一個,就會被掃描

關注公眾號領資料

搜索公眾號【Java耕耘者】,回覆【Java】,即可獲取大量優質電子書和一份Java高級架構資料、Spring源碼分析、Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式等視頻資料

"

相關推薦

推薦中...