http://www.javashuo.com/article/p-uvudtich-bm.htmlhtml
上一篇文章中,咱們經過createApplicationContext方法建立了一個ApplicationContext的實例對象。本文將閱讀一下在ApplicationContext在refresh以前的prepareContext中作了哪些事情。spring
咱們跟進prepareContext方法springboot
private void prepareContext( ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner ) { // 設置Environment context.setEnvironment(environment); // 後置處理 postProcessApplicationContext(context); // 調用ApplicationContextInitializer接口的實現 applyInitializers(context); // 發佈ApplicationContext準備事件 listeners.contextPrepared(context); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 註冊args參數爲單例bean beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { // 註冊banner爲單例bean beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { // 設置beanFactory中是否容許重複bean覆蓋 ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 加載main方法所在類 Set<Object> sources = getAllSources(); // 註冊main方法所在類到beanFactory load(context, sources.toArray(new Object[0])); // 發佈Context加載事件 listeners.contextLoaded(context); }
咱們看到prepareContext方法主要邏輯包含了三塊內容app
1)基本的初始化,如設置Environment,調用ApplicationContextInitializer接口的實現類post
2)註冊現有的對象爲單例bean,如args、bannerthis
3)加載main方法所在的主類spa
很顯然,加載main方法的主類是咱們關注的重點。咱們先看看getAllSources方法返回code
public Set<Object> getAllSources() { Set<Object> allSources = new LinkedHashSet<>(); // 添加主類 if (!CollectionUtils.isEmpty(this.primarySources)) { allSources.addAll(this.primarySources); } // 添加附加資源類 if (!CollectionUtils.isEmpty(this.sources)) { allSources.addAll(this.sources); } return Collections.unmodifiableSet(allSources); }
primarySources是在SpringApplication初始化的時候設置的,而sources默認是沒有的。所在getAllSoures方法將返回main方法所在的主類。xml
下面,咱們跟進load方法,閱讀一下加載main所在主類的邏輯htm
protected void load(ApplicationContext context, Object[] sources) { // 獲取BeanDefinition加載器 BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } // 加載資源 loader.load(); }
load方法中,先是得到了BeanDefinitionLoader,而後去加載資源。這裏要注意!爲何是BeanDefinitionLoader呢?
首先,咱們得知道Spring的bean資源來自各類地方,如xml、annotation等,那麼這些bean在配置的時候也就是對bean進行定義,而這些定義映射到內存中的對象就是BeanDefinition的對象,Spring後續會根據BeanDefinition再獲取具體的bean。
簡單來講就是:配置 --> BeanDefinition --> Bean 這樣一個邏輯
因此,後續咱們會看到BeanDefinitionLoader會將主類加載成BeanDefinition,而後註冊到ApplicationContext當中。
先跟進getBeanDefinitionRegistry方法,看看BeanDefinition會被註冊到哪裏去
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) { // 返回當前ApplicationContext if (context instanceof BeanDefinitionRegistry) { return (BeanDefinitionRegistry) context; } if (context instanceof AbstractApplicationContext) { return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory(); } throw new IllegalStateException("Could not locate BeanDefinitionRegistry"); }
咱們注意,springboot的AnnotationConfigServletWebServerApplicationContext這個ApplicationContext的實現類,隨着繼承鏈路向上走是繼承自GenericApplicationContext的,而GenericApplicationContext實現了BeanDefinitionRegistry。
因此,getBeanDefinitionRegistry將最終返回強轉過的ApplicationContext。也就是說BeanDefinition將被註冊到ApplicationContext裏面。
回到load方法中,咱們再跟進createBeanDefinitionLoader方法
protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) { return new BeanDefinitionLoader(registry, sources); }
再跟進構造方法
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { this.sources = sources; // 註解方式的讀取器 this.annotatedReader = new AnnotatedBeanDefinitionReader(registry); // xml方式的讀取器 this.xmlReader = new XmlBeanDefinitionReader(registry); // 類路徑下的掃描器 this.scanner = new ClassPathBeanDefinitionScanner(registry); // 掃描排除當前main方法的主類 this.scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }
咱們看到加載器支持註解、xml兩種方式。類路徑下的掃描器排除了當前的主類
回到load方法
protected void load(ApplicationContext context, Object[] sources) { // 獲取BeanDefinition加載器 BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } // 加載資源 loader.load(); }
此時,咱們已經獲取了BeanDefinitionLoader,下面調用該loader的load方法開始加載
跟進第二個load方法
public int load() { int count = 0; for (Object source : this.sources) { count += load(source); } return count; }
再跟進第三個load方法
private int load(Object source) { if (source instanceof Class<?>) { return load((Class<?>) source); } if (source instanceof Resource) { return load((Resource) source); } if (source instanceof Package) { return load((Package) source); } if (source instanceof CharSequence) { return load((CharSequence) source); } throw new IllegalArgumentException("Invalid source type " + source.getClass()); }
因爲咱們的主類是一個class,因此進入第一個if分支的load方法
繼續跟進
private int load(Class<?> source) { // 省略 if (isComponent(source)) { this.annotatedReader.register(source); return 1; } return 0; }
該方法先經過isComponent方法判斷了主類是否是被@Component註解,若是是,那麼調用註解方式的閱讀器,註冊該資源。
跟進isComponent方法,看看怎麼判斷的
private boolean isComponent(Class<?> type) { // 找到是否匹配@Component註解 if (AnnotationUtils.findAnnotation(type, Component.class) != null) { return true; } // 省略 }
其實就是找這個類是否有@Component註解,但請注意咱們一般都使用@SpringBootApplication這個註解,並無直接註解@Component。而@SpringBootApplication是一個組合註解,其中就組合了@Component
而AnnotationUtils.findAnnotation方法將會遞歸遍歷註解,最終找到@Component。
isComponent判斷爲true之後,咱們再跟進annotationReader.register(source)閱讀一下讀取主類的過程
public void register(Class<?>... annotatedClasses) { for (Class<?> annotatedClass : annotatedClasses) { registerBean(annotatedClass); } }
繼續跟進registerBean方法
public void registerBean(Class<?> annotatedClass) { doRegisterBean(annotatedClass, null, null, null); }
再跟進doRegisterBean方法,該方法比較長,咱們省略掉一些次要的部分
<T> void doRegisterBean( Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers ) { // 先包裝成BeanDefinition AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); // 解析scope元數據 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); // 生成bean的名 String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // 解析一些常見的註解元數據 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 註冊BeanDefinition到ApplicationContext BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
能夠看到,doRegisterBean方法的主要邏輯就是包裝並解析出一個BeanDefinition,而後調用registerBeanDefinition方法把BeanDefinition給註冊到ApplicationContext中。
註冊相關的本文就再也不繼續展開了,後續的文章會跟進這些內容。
總的來講,prepareContext方法主要就是爲了加載並註冊主類的BeanDefinition到ApplicationContext。這裏注意!咱們一直都在說註冊到ApplicationContext,但熟悉spring的都會知道不管是Bean仍是BeanDefinition都是註冊到BeanFactory中的。但咱們一直沒有嚴格區分它,後續的文章咱們將會把ApplicationContext和BeanFactory進行區分。