您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關Spring中IoC源碼怎么寫,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
IoC 全稱為 Inversion of Control,翻譯為 “控制反轉”,它還有一個別名為 DI(Dependency Injection),即依賴注入。
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配置,更加便捷。
主要類或接口說明:
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——注解配置注冊表。用于注解配置應用上下文的通用接口,擁有一個注冊配置類和掃描配置類的方法。
//默認構造函數,初始化一個空容器,容器不包含任何 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方法初始化容器
這里我們用的是最后一種構造函數,即傳入一個包路徑。
首先看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個注解中的任意一個,就會被掃描
以上就是Spring中IoC源碼怎么寫,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。