這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)springboot中如何初始化上下文構(gòu)建類,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、成都微信小程序、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了民和免費(fèi)建站歡迎大家使用!
文章基于springboot2.3.x系列的源碼(大部分以jar包中的源碼為例講解),github的源碼與實(shí)際發(fā)版的可能略微不同,不過(guò)整體流程差別不大。
源碼:
protected ConfigurableApplicationContext createApplicationContext() { Class> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch(this.webApplicationType) { case SERVLET: contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"); break; case REACTIVE: contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"); break; default: contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext"); } } catch (ClassNotFoundException var3) { throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3); } } return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass); }
webApplicationType默認(rèn)值為SERVLET,這里對(duì)應(yīng)會(huì)獲取AnnotationConfigServletWebServerApplicationContext的class對(duì)象。通過(guò)BeanUtils.instantiateClass實(shí)例化對(duì)象,其實(shí)直接通過(guò)new創(chuàng)建出來(lái)效果一樣,并且在以后版本的源碼中是直接使用new創(chuàng)建的。這個(gè)類的作用通過(guò)名稱也可以知道,是基于注解形式的web上下文應(yīng)用構(gòu)建。與之相對(duì)應(yīng)的是xml方式XmlServletWebServerApplicationContext。
先看一下這個(gè)關(guān)鍵類的uml圖:
這里圖片上的字比較小,如果看不清也可以通過(guò)自己的idea去查看。關(guān)系比較復(fù)雜,但是可以在圖上看到幾個(gè)常見(jiàn)的類。BeanFactory、ResourceLoader、lifecycle等??梢钥闯鲰攲拥亩际墙涌冢旅娴膶?shí)現(xiàn)類也相互交叉,其公共的父類之一是ApplicationContext接口。
這里開(kāi)始我們需要去了解AnnotationConfigServletWebServerApplicationContext的自身和其父類,因?yàn)榈竭@里開(kāi)始,有很多初始化動(dòng)作已經(jīng)在構(gòu)造函數(shù)內(nèi)進(jìn)行了。
從自身開(kāi)始:
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner; private final Set> annotatedClasses; private String[] basePackages; public AnnotationConfigServletWebServerApplicationContext() { this.annotatedClasses = new LinkedHashSet(); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } ...... //還有其他的構(gòu)造函數(shù),這里我們只選啟動(dòng)流程默認(rèn)的無(wú)參構(gòu)造函數(shù) }
構(gòu)造函數(shù)內(nèi)初始化3個(gè)參數(shù),其中 AnnotatedBeanDefinitionReader和 ClassPathBeanDefinitionScanner是兩個(gè)非常關(guān)鍵的類。一個(gè)是基于注解的bean屬性定義,一個(gè)是基于路徑的bean定義掃描。
我們老樣子逐個(gè)進(jìn)行分析。從這里也開(kāi)始了無(wú)限套娃模式。說(shuō)實(shí)話spring系列的源碼寫(xiě)的真的很好,但也真的很難讀懂。
AnnotatedBeanDefinitionReader: 從構(gòu)造函數(shù)開(kāi)始:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { //基于產(chǎn)生bean的注解,若沒(méi)有指定bean的名稱將會(huì)自動(dòng)生成一個(gè)首字母小寫(xiě)的名稱作為bean的名稱 this.beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE; //Scop的代理模式 this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(); Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); //這里指的是當(dāng)前的環(huán)境變量 this.registry = registry; //條件注入bean this.conditionEvaluator = new ConditionEvaluator(registry, environment, (ResourceLoader)null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); return (Environment)(registry instanceof EnvironmentCapable ? ((EnvironmentCapable)registry).getEnvironment() : new StandardEnvironment()); }
先看一下getOrCreateEnvironment方法: 方法主要是判斷了一下當(dāng)前AnnotationConfigServletWebServerApplicationContext是否是EnvironmentCapable子類,若是則返回當(dāng)前環(huán)境對(duì)象,若不是則初始化一個(gè)StandardEnvironment。
EnvironmentCapable主要是判斷當(dāng)前上限文是否具有提供環(huán)境屬性的能力。內(nèi)部只有一個(gè)getEnvironment方法,原設(shè)計(jì)是獲取一個(gè)不可修改的環(huán)境對(duì)象。但是在ConfigurableApplicationContext對(duì)其進(jìn)行了重寫(xiě),使獲取的對(duì)象具有可再次定制的能力。
吐槽一下很有迷惑的入?yún)?strong>BeanDefinitionRegistry,這個(gè)接口與EnvironmentCapable沒(méi)有任何的關(guān)系,BeanDefinitionRegistry相當(dāng)于我們?cè)趯?xiě)代碼時(shí)用的Object接受參數(shù)一樣,是ApplicationContext的父類。但是方法內(nèi)直接進(jìn)行了類型強(qiáng)轉(zhuǎn),雖然也沒(méi)有錯(cuò)。
AnnotationBeanNameGenerator:
基于注解生成bean的名稱。若注解指定了則使用注解內(nèi)的,否則默認(rèn)將類名的首字母變成小寫(xiě)作為名稱。
AnnotationScopeMetadataResolver:
設(shè)置bean的代理模式,即我們常用的@Scop注解的實(shí)現(xiàn)。在其構(gòu)造函數(shù)內(nèi)默認(rèn)設(shè)置ScopedProxyMode.NO不使用代理。也就是單例模式,即bean是全局唯一的這樣可以避免反復(fù)的創(chuàng)建bean,提升服務(wù)性能,但也存在線程安全問(wèn)題,其實(shí)很好理解,單例的bean也意味著若存在全局變量,就不能隨意更改否則變更后會(huì)有影響。
ScopedProxyMode共有四種類型:
DEFAULT:等同于NO,不使用代理的單例模式。
NO:?jiǎn)卫J?br/>INTERFACES:jdk動(dòng)態(tài)代理
TARGET_CLASS:CGLAB動(dòng)態(tài)代理
ConditionEvaluator:
自定義創(chuàng)建bean條件,注解@Condition的處理類??梢园凑諚l件進(jìn)行類加載,條件需要單獨(dú)定義,并實(shí)現(xiàn) Condition接口。
例如:
public class MyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { .... return true; } } @Conditional({ MyCondition.class }) @Configuration public class MyConfig { @Bean @Conditional({ MyCondition.class }) public Date date() { return new Date(); } }
在springboot項(xiàng)目中很少使用了,springboot提供了更多的@Condition的擴(kuò)展注解,例如:@ConditionalOnMissingBean,@ConditionalOnProperty,@ConditionalOnBean等。
AnnotationConfigUtils.registerAnnotationConfigProcessors:
在給定的注冊(cè)表中注冊(cè)所有相關(guān)的注解后處理器。比如提供@Autowired,@Value,@Order等注解的支持。所謂的后處理器,其實(shí)就是相關(guān)注解的支持類。
源碼:
public static SetregisterAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { //獲取bean工廠 若是springboot我們這里已經(jīng)知道其為 DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { //加載順序機(jī)制 if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } //決定一個(gè)bean定義是否應(yīng)該被視為自動(dòng)裝配的候選時(shí),為這個(gè)BeanFactory設(shè)置一個(gè)自定義的自動(dòng)裝配候選解析器 if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set beanDefs = new LinkedHashSet<>(8); //驗(yàn)證是否包含名稱org.springframework.context.annotation.internalConfigurationAnnotationProcessor的bean //若沒(méi)有則通過(guò)registerPostProcessor方法添加到registry中 if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { //將需要注冊(cè)的bean手動(dòng)封裝成RootBeanDefinition里面,用于描述bean的屬性和對(duì)bean進(jìn)行操作 //@configuration支持 RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { //@Autowired、Value注解支持 RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. //JSR-250是一個(gè)標(biāo)準(zhǔn),可以理解為java的約定或者說(shuō)預(yù)留的注解,網(wǎng)上的定義為資源相關(guān)的處理 if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. // JPA約定 if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } //異步監(jiān)聽(tīng)器 if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } //監(jiān)聽(tīng)器工廠 if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; }
源碼看起來(lái)比較多 但實(shí)際上邏輯很簡(jiǎn)單,為了初始化一些spring自身資源類注解的支持,并注入成bean。類似于@Resource,@Value等,bean內(nèi)屬性的注入,并非@Service的類級(jí)注解。
格外提一點(diǎn)擴(kuò)展知識(shí),上面的源碼中能看到一個(gè)jsr250Present 和 jpaPresent 著其實(shí)是java的兩個(gè)約定。感興趣的可以去了解一下,我只說(shuō)一點(diǎn),比如:比如我們知道@Resource和@Autowired在很多情況下是可以互換的。
但是@Resource并非spring的注解,而是java自帶的。很早以前就很困惑,為何spring要提供一個(gè)java自身注解的支持,難道不會(huì)和其他的框架或者jar沖突嗎?然而事實(shí)上的確會(huì)沖突,但這個(gè)@Resource是java制定的資源加載約定注解,相當(dāng)于我提供了一個(gè)接口在這,并且我建議你們遵循這個(gè)約定去實(shí)現(xiàn)它。
這樣的注解不止@Resource一個(gè),感興趣的可以去了解一下。同理JPA也類似。
單看方法的話會(huì)有點(diǎn)奇怪,這里的返回值不重要,關(guān)鍵是registerPostProcessor方法。
源碼:
private static BeanDefinitionHolder registerPostProcessor( BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) { definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); //注入bean registry.registerBeanDefinition(beanName, definition); return new BeanDefinitionHolder(definition, beanName); }
方法內(nèi)registry.registerBeanDefinition注冊(cè)了spring的內(nèi)部bean。
這里講解的順序有問(wèn)題,下一篇我們將從頂層往下看,這篇是從底層往上看因此,會(huì)有點(diǎn)莫名其妙。若看過(guò)源碼或調(diào)試過(guò)的小伙伴應(yīng)該知道到這里的時(shí)候其實(shí)已經(jīng)知道了,當(dāng)前上下文中使用的beanFactory是DefaultListableBeanFactory這是一個(gè)非常關(guān)鍵的類,bean的整個(gè)加載流程都在內(nèi)部,包括所謂的IOC容器,其實(shí)就是一個(gè)Map,這些都是后話了。
AnnotatedBeanDefinitionReader類的解析告一段落。
ClassPathBeanDefinitionScanner:
AnnotatedBeanDefinitionReader實(shí)現(xiàn)了bean內(nèi)屬性的組裝。ClassPathBeanDefinitionScanner實(shí)現(xiàn)的是bean的掃描和加載。
構(gòu)造函數(shù)源碼:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { .... 與AnnotatedBeanDefinitionReader相同 this.registry = registry; if (useDefaultFilters) { this.registerDefaultFilters(); } } protected void registerDefaultFilters() { //設(shè)置Component注解過(guò)濾器 this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.annotation.ManagedBean", cl), false)); this.logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException var4) { } try { this.includeFilters.add(new AnnotationTypeFilter(ClassUtils.forName("javax.inject.Named", cl), false)); this.logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException var3) { } }
提供:Component、Repository、Service、Controller注解的支持,源碼中只看到了配置Component,因?yàn)槠渌麕讉€(gè)注解都基于Component擴(kuò)展的,另外提供了javaEE 6的ManagedBean注解和JSR-330的Named注解支持。 感興趣的可以去了解一下 JSR和JPA協(xié)議。
AnnotationConfigServletWebServerApplicationContext類講解到此結(jié)束。
回到上面UMl圖,可以看到其擁有一個(gè)復(fù)雜的繼承關(guān)系,并且上面也留了一個(gè)疑問(wèn),加載過(guò)程中的beanFactory是在什么地方初始化的?
下面將依次看一下其父類的無(wú)參構(gòu)造函數(shù)都做了啥(接口除外),大部分接口內(nèi)部沒(méi)有邏輯代碼。
按從下往上的方式去看:
ServletWebServerApplicationContext無(wú)操作。
GenericWebApplicationContext無(wú)操作
GenericApplicationContext
源碼:
public GenericApplicationContext() { this.customClassLoader = false; this.refreshed = new AtomicBoolean(); // 初始化bean工廠 this.beanFactory = new DefaultListableBeanFactory(); }
這里已經(jīng)知道上下文中使用的beanFactory是DefaultListableBeanFactory類
AbstractApplicationContext
源碼:
public AbstractApplicationContext() { this.logger = LogFactory.getLog(this.getClass()); this.id = ObjectUtils.identityToString(this); this.displayName = ObjectUtils.identityToString(this); this.beanFactoryPostProcessors = new ArrayList(); this.active = new AtomicBoolean(); this.closed = new AtomicBoolean(); this.startupShutdownMonitor = new Object(); this.applicationListeners = new LinkedHashSet(); //特殊資源路徑的加載處理,非classpath*:下的資源,如:file:C:/context.xml this.resourcePatternResolver = this.getResourcePatternResolver(); }
DefaultResourceLoader
源碼:
public DefaultResourceLoader() { this.classLoader = ClassUtils.getDefaultClassLoader(); }
構(gòu)造函數(shù)源碼:
注釋理解來(lái)自于源碼:
public DefaultListableBeanFactory() { // 用于檢查bean定義是否為自動(dòng)裝配候選的解析程序 this.autowireCandidateResolver = SimpleAutowireCandidateResolver.INSTANCE; //從依賴項(xiàng)類型映射到相應(yīng)的自動(dòng)裝配值 this.resolvableDependencies = new ConcurrentHashMap(16); //Bean定義對(duì)象的映射,以Bean名稱為鍵。 this.beanDefinitionMap = new ConcurrentHashMap(256); //根據(jù)名稱合并 BeanDefinitionHolder持有的bean this.mergedBeanDefinitionHolders = new ConcurrentHashMap(256); //單例和非單例Bean名稱的映射,按依賴項(xiàng)類型進(jìn)行鍵控 this.allBeanNamesByType = new ConcurrentHashMap(64); //僅依賴單例的bean名稱的映射,按依賴項(xiàng)類型進(jìn)行鍵控 this.singletonBeanNamesByType = new ConcurrentHashMap(64); //Bean定義名稱列表,按注冊(cè)順序 this.beanDefinitionNames = new ArrayList(256); //手動(dòng)注冊(cè)的單例的名稱列表,按注冊(cè)順序。 this.manualSingletonNames = new LinkedHashSet(16); }
這個(gè)是jar包中的源碼,若從github獲取的源碼構(gòu)造函數(shù)內(nèi)可能并沒(méi)有,而是通過(guò)餓漢式的模式直接在定義屬性的時(shí)候就初始化了,這樣雖然寫(xiě)著方便,但看著并不直觀。
構(gòu)造函數(shù)內(nèi)初始化的Map們就是口中常說(shuō)的IOC容器
那么bean的循環(huán)依賴時(shí)如何被解決的呢?先留一個(gè)疑問(wèn),在將bean加載的流程時(shí)候再說(shuō)。
DefaultListableBeanFactory也有著很復(fù)雜的繼承關(guān)系,下面將結(jié)合UML圖進(jìn)行講解。
只看圖中藍(lán)色的實(shí)體類繼承關(guān)系。逐個(gè)類的構(gòu)造函數(shù)查看。構(gòu)造函數(shù)未必都是無(wú)參的,具體執(zhí)行的哪一個(gè)要看子類是否有傳入特定參數(shù)。
一般情況下,在非工具的實(shí)體類中很少有 static 的靜態(tài)代碼塊,因此不需要刻意去尋找。一般都是通過(guò)構(gòu)造函數(shù)完成初始化。
AbstractAutowireCapableBeanFactory
源碼:
public AbstractAutowireCapableBeanFactory() { //基于CGLB的bean動(dòng)態(tài)代理創(chuàng)建(策略模式) this.instantiationStrategy = new CglibSubclassingInstantiationStrategy(); //獲取方法或構(gòu)造函數(shù)的參數(shù)名稱,這里默認(rèn)通過(guò)java反射機(jī)制 this.parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); //是否自動(dòng)嘗試解析bean之間的循環(huán)依賴 this.allowCircularReferences = true; //在循環(huán)依賴的時(shí)候,是否可以注入原始的bean,應(yīng)該是為進(jìn)行屬性賦值的原始bean this.allowRawInjectionDespiteWrapping = false; //根據(jù)類型忽略相關(guān)的實(shí)現(xiàn) this.ignoredDependencyTypes = new HashSet(); //忽略相關(guān)接口 this.ignoredDependencyInterfaces = new HashSet(); // 當(dāng)前創(chuàng)建的bean的名稱,用于隱式依賴項(xiàng)注冊(cè) // 從用戶指定的Supplier回調(diào)觸發(fā)的getBean等調(diào)用上 this.currentlyCreatedBean = new NamedThreadLocal("Currently created bean"); //未完成的FactoryBean實(shí)例的緩存:BeanWrapper的FactoryBean名稱 this.factoryBeanInstanceCache = new ConcurrentHashMap(); //按工廠類別緩存候選工廠方法 this.factoryMethodCandidateCache = new ConcurrentHashMap(); //過(guò)濾后的PropertyDescriptor的緩存:Bean類到PropertyDescriptor數(shù)組。 this.filteredPropertyDescriptorsCache = new ConcurrentHashMap(); this.ignoreDependencyInterface(BeanNameAware.class); this.ignoreDependencyInterface(BeanFactoryAware.class); this.ignoreDependencyInterface(BeanClassLoaderAware.class); }
源碼中初始化了bean的代理模式、bean循環(huán)依賴處理配置,以及緩存bean的相關(guān)工廠信息。 最后忽略了BeanNameAware、BeanFactoryAware、 BeanClassLoaderAware三個(gè)接口的相關(guān)類。
暫時(shí)原因并不清楚,為何要排除。這里暫時(shí)先不說(shuō),等說(shuō)道bean加載的時(shí)候在說(shuō)。
主要幾點(diǎn):
1.資源類注解@Resouce、@Value等,背后的支持類初始化完成。 2.bean加載相關(guān)注解,@Component、@Service,背后的支持類初始化。 3.基于condition的條件注入 4.DefaultListableBeanFactory工廠類的初始化。所謂IOC容器也是在這里進(jìn)行初始化。且內(nèi)部涉及到bean的循環(huán)依賴配置。
上述就是小編為大家分享的springboot中如何初始化上下文構(gòu)建類了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。