Spring 之 IoC 原始碼分析 (基於註解方式)
一、 IoC 理論
IoC 全稱為 Inversion of Control,翻譯為 “控制反轉”,它還有一個別名為 DI(Dependency Injection),即依賴注入。
二、IoC方式
Spring為IoC提供了2種方式,一種是基於xml,另一種是基於註解。
- <Bean>標籤來定義bean,進行管理。
- @Bean註解來定義bean,進行管理。
本次文章我們就來分析下基於註解的IoC原理,在看文章之前我們可以帶一些疑問,這樣有助於我們更好的理解。
- @Bean是幹什麼用的?
- @Controller、@Service又是幹啥的?
- @CompoentScan註解是怎麼起作用的?
- Spring是怎麼發現@Bean、@Controller、@Service這些註解修飾的類的?
- 發現之後是怎麼註冊到IOC容器中的?
- IOC容器到底是個啥?
三、原始碼分析
首先看下段程式碼:
AnnotationConfigApplicationContext aac =
new AnnotationConfigApplicationContext("com.mydemo");
AnnotationConfigApplicationContext可以實現基於Java的配置類(包括各種註解)載入Spring的應用上下文。避免使用application.xml進行配置。相比XML配置,更加便捷。
3.1、類結構圖
主要類或介面說明:
-
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()
//方法註冊配置類,並呼叫refresh()方法重新整理容器,觸發容器對註解Bean的載入、解析和註冊過程
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
//最常用的建構函式,通過將涉及到的配置類傳遞給該建構函式,以實現將相應配置類中的Bean自動註冊到容器中
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
//呼叫無參建構函式,初始化AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner
this();
register(annotatedClasses);
refresh();
}
//該建構函式會自動掃描以給定的包及其子包下的所有類,並自動識別所有的Spring Bean,將其註冊到容器中
public AnnotationConfigApplicationContext(String... basePackages) {
//初始化ClassPathBeanDefinitionScanner和AnnotatedBeanDefinitionReader
this();//step1
//掃描包、註冊bean
scan(basePackages);//step2
refresh();//step3
}
主要屬性:
-
AnnotatedBeanDefinitionReader——BeanDefinition解析器用來解析帶註解的bean
-
ClassPathBeanDefinitionScanner——bean的掃描器 用來掃描類
-
註冊解析傳入的配置類(使用類配置的方式進行解析)
-
呼叫容器的refresh方法初始化容器
這裡我們用的是最後一種建構函式,即傳入一個包路徑。
3.3 IoC 之 建構函式初始化
首先看step1,呼叫了本類的無參建構函式:
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
然後初始化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner
我們來看下ClassPathBeanDefinitionScanner的建構函式
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this(registry, true);
}
繼續跟蹤下去,最後呼叫的是這個方法:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//為容器設定載入Bean定義的註冊器
this.registry = registry;
//是否使用預設過濾規則
if (useDefaultFilters) {
registerDefaultFilters();
}
//設定環境
setEnvironment(environment);
//為容器設定資源載入器
setResourceLoader(resourceLoader);
}
這裡面最主要的是registerDefaultFilters()方法,初始化spring掃描預設過濾規則,對應@ComponentScan註解,如果沒有自定義規則,就初始化預設過濾規則。
這裡呼叫的是ClassPathScanningCandidateComponentProvider類中的registerDefaultFilters()方法:
//向容器註冊過濾規則
@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
//向要包含的過濾規則中新增@Component註解類
//@Service和@Controller都是Component,因為這些註解都添加了@Component註解
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
//獲取當前類的類載入器
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
//向要包含的過濾規則新增JavaEE6的@ManagedBean註解
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
//向要包含的過濾規則新增@Named註解
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
這裡面有兩個關鍵變數:
-
private final List<TypeFilter> includeFilters = new LinkedList<>();
-
private final List<TypeFilter> excludeFilters = new LinkedList<>();
includeFilters表示要包含的註解,即只有包含includeFilters中的註解,才會被掃描
excludeFilters表示要排除的註解,即包含excludeFilters中的註解不會被掃描
在這個方法中,includeFilters集合中添加了@Component、JavaEE6的@ManagedBean和JSR-330的@Named註解
而excludeFilters集合沒做任何變動,即沒有要排除的註解
總結:
所以預設規則就是,只要包含了@Component、JavaEE6的@ManagedBean和JSR-330的@Named這3個註解中的任意一個,就