您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關springboot中如何初始化上下文構建類,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
文章基于springboot2.3.x系列的源碼(大部分以jar包中的源碼為例講解),github的源碼與實際發版的可能略微不同,不過整體流程差別不大。
源碼:
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默認值為SERVLET,這里對應會獲取AnnotationConfigServletWebServerApplicationContext的class對象。通過BeanUtils.instantiateClass實例化對象,其實直接通過new創建出來效果一樣,并且在以后版本的源碼中是直接使用new創建的。這個類的作用通過名稱也可以知道,是基于注解形式的web上下文應用構建。與之相對應的是xml方式XmlServletWebServerApplicationContext。
先看一下這個關鍵類的uml圖:
這里圖片上的字比較小,如果看不清也可以通過自己的idea去查看。關系比較復雜,但是可以在圖上看到幾個常見的類。BeanFactory、ResourceLoader、lifecycle等。可以看出頂層的都是接口,下面的實現類也相互交叉,其公共的父類之一是ApplicationContext接口。
這里開始我們需要去了解AnnotationConfigServletWebServerApplicationContext的自身和其父類,因為到這里開始,有很多初始化動作已經在構造函數內進行了。
從自身開始:
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner; private final Set<Class<?>> annotatedClasses; private String[] basePackages; public AnnotationConfigServletWebServerApplicationContext() { this.annotatedClasses = new LinkedHashSet(); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } ...... //還有其他的構造函數,這里我們只選啟動流程默認的無參構造函數 }
構造函數內初始化3個參數,其中 AnnotatedBeanDefinitionReader和 ClassPathBeanDefinitionScanner 是兩個非常關鍵的類。一個是基于注解的bean屬性定義,一個是基于路徑的bean定義掃描。
我們老樣子逐個進行分析。從這里也開始了無限套娃模式。說實話spring系列的源碼寫的真的很好,但也真的很難讀懂。
AnnotatedBeanDefinitionReader: 從構造函數開始:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { //基于產生bean的注解,若沒有指定bean的名稱將會自動生成一個首字母小寫的名稱作為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"); //這里指的是當前的環境變量 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方法: 方法主要是判斷了一下當前AnnotationConfigServletWebServerApplicationContext是否是EnvironmentCapable子類,若是則返回當前環境對象,若不是則初始化一個StandardEnvironment。
EnvironmentCapable主要是判斷當前上限文是否具有提供環境屬性的能力。內部只有一個getEnvironment方法,原設計是獲取一個不可修改的環境對象。但是在ConfigurableApplicationContext對其進行了重寫,使獲取的對象具有可再次定制的能力。
吐槽一下很有迷惑的入參BeanDefinitionRegistry,這個接口與EnvironmentCapable沒有任何的關系,BeanDefinitionRegistry相當于我們在寫代碼時用的Object接受參數一樣,是ApplicationContext的父類。但是方法內直接進行了類型強轉,雖然也沒有錯。
AnnotationBeanNameGenerator:
基于注解生成bean的名稱。若注解指定了則使用注解內的,否則默認將類名的首字母變成小寫作為名稱。
AnnotationScopeMetadataResolver:
設置bean的代理模式,即我們常用的@Scop注解的實現。在其構造函數內默認設置ScopedProxyMode.NO 不使用代理。也就是單例模式,即bean是全局唯一的這樣可以避免反復的創建bean,提升服務性能,但也存在線程安全問題,其實很好理解,單例的bean也意味著若存在全局變量,就不能隨意更改否則變更后會有影響。
ScopedProxyMode共有四種類型:
DEFAULT:等同于NO,不使用代理的單例模式。
NO:單例模式
INTERFACES:jdk動態代理
TARGET_CLASS:CGLAB動態代理
ConditionEvaluator:
自定義創建bean條件,注解@Condition的處理類。可以按照條件進行類加載,條件需要單獨定義,并實現 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項目中很少使用了,springboot提供了更多的@Condition的擴展注解,例如:@ConditionalOnMissingBean,@ConditionalOnProperty,@ConditionalOnBean等。
AnnotationConfigUtils.registerAnnotationConfigProcessors:
在給定的注冊表中注冊所有相關的注解后處理器。比如提供@Autowired,@Value,@Order等注解的支持。所謂的后處理器,其實就是相關注解的支持類。
源碼:
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { //獲取bean工廠 若是springboot我們這里已經知道其為 DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { //加載順序機制 if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } //決定一個bean定義是否應該被視為自動裝配的候選時,為這個BeanFactory設置一個自定義的自動裝配候選解析器 if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); //驗證是否包含名稱org.springframework.context.annotation.internalConfigurationAnnotationProcessor的bean //若沒有則通過registerPostProcessor方法添加到registry中 if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { //將需要注冊的bean手動封裝成RootBeanDefinition里面,用于描述bean的屬性和對bean進行操作 //@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是一個標準,可以理解為java的約定或者說預留的注解,網上的定義為資源相關的處理 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)); } //異步監聽器 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)); } //監聽器工廠 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; }
源碼看起來比較多 但實際上邏輯很簡單,為了初始化一些spring自身資源類注解的支持,并注入成bean。類似于@Resource,@Value等,bean內屬性的注入,并非@Service的類級注解。
格外提一點擴展知識,上面的源碼中能看到一個jsr250Present 和 jpaPresent 著其實是java的兩個約定。感興趣的可以去了解一下,我只說一點,比如:比如我們知道@Resource和@Autowired在很多情況下是可以互換的。
但是@Resource并非spring的注解,而是java自帶的。很早以前就很困惑,為何spring要提供一個java自身注解的支持,難道不會和其他的框架或者jar沖突嗎?然而事實上的確會沖突,但這個@Resource是java制定的資源加載約定注解,相當于我提供了一個接口在這,并且我建議你們遵循這個約定去實現它。
這樣的注解不止@Resource一個,感興趣的可以去了解一下。同理JPA也類似。
單看方法的話會有點奇怪,這里的返回值不重要,關鍵是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); }
方法內registry.registerBeanDefinition注冊了spring的內部bean。
這里講解的順序有問題,下一篇我們將從頂層往下看,這篇是從底層往上看因此,會有點莫名其妙。若看過源碼或調試過的小伙伴應該知道到這里的時候其實已經知道了,當前上下文中使用的beanFactory是DefaultListableBeanFactory 這是一個非常關鍵的類,bean的整個加載流程都在內部,包括所謂的IOC容器,其實就是一個Map,這些都是后話了。
AnnotatedBeanDefinitionReader類的解析告一段落。
ClassPathBeanDefinitionScanner:
AnnotatedBeanDefinitionReader實現了bean內屬性的組裝。ClassPathBeanDefinitionScanner實現的是bean的掃描和加載。
構造函數源碼:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { .... 與AnnotatedBeanDefinitionReader相同 this.registry = registry; if (useDefaultFilters) { this.registerDefaultFilters(); } } protected void registerDefaultFilters() { //設置Component注解過濾器 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,因為其他幾個注解都基于Component擴展的,另外提供了javaEE 6的ManagedBean注解和JSR-330的Named注解支持。 感興趣的可以去了解一下 JSR和JPA協議。
AnnotationConfigServletWebServerApplicationContext 類講解到此結束。
回到上面UMl圖,可以看到其擁有一個復雜的繼承關系,并且上面也留了一個疑問,加載過程中的beanFactory是在什么地方初始化的?
下面將依次看一下其父類的無參構造函數都做了啥(接口除外),大部分接口內部沒有邏輯代碼。
按從下往上的方式去看:
ServletWebServerApplicationContext 無操作。
GenericWebApplicationContext 無操作
GenericApplicationContext
源碼:
public GenericApplicationContext() { this.customClassLoader = false; this.refreshed = new AtomicBoolean(); // 初始化bean工廠 this.beanFactory = new DefaultListableBeanFactory(); }
這里已經知道上下文中使用的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(); }
構造函數源碼:
注釋理解來自于源碼:
public DefaultListableBeanFactory() { // 用于檢查bean定義是否為自動裝配候選的解析程序 this.autowireCandidateResolver = SimpleAutowireCandidateResolver.INSTANCE; //從依賴項類型映射到相應的自動裝配值 this.resolvableDependencies = new ConcurrentHashMap(16); //Bean定義對象的映射,以Bean名稱為鍵。 this.beanDefinitionMap = new ConcurrentHashMap(256); //根據名稱合并 BeanDefinitionHolder持有的bean this.mergedBeanDefinitionHolders = new ConcurrentHashMap(256); //單例和非單例Bean名稱的映射,按依賴項類型進行鍵控 this.allBeanNamesByType = new ConcurrentHashMap(64); //僅依賴單例的bean名稱的映射,按依賴項類型進行鍵控 this.singletonBeanNamesByType = new ConcurrentHashMap(64); //Bean定義名稱列表,按注冊順序 this.beanDefinitionNames = new ArrayList(256); //手動注冊的單例的名稱列表,按注冊順序。 this.manualSingletonNames = new LinkedHashSet(16); }
這個是jar包中的源碼,若從github獲取的源碼構造函數內可能并沒有,而是通過餓漢式的模式直接在定義屬性的時候就初始化了,這樣雖然寫著方便,但看著并不直觀。
構造函數內初始化的Map們就是口中常說的IOC容器
那么bean的循環依賴時如何被解決的呢?先留一個疑問,在將bean加載的流程時候再說。
DefaultListableBeanFactory也有著很復雜的繼承關系,下面將結合UML圖進行講解。
只看圖中藍色的實體類繼承關系。逐個類的構造函數查看。構造函數未必都是無參的,具體執行的哪一個要看子類是否有傳入特定參數。
一般情況下,在非工具的實體類中很少有 static 的靜態代碼塊,因此不需要刻意去尋找。一般都是通過構造函數完成初始化。
AbstractAutowireCapableBeanFactory
源碼:
public AbstractAutowireCapableBeanFactory() { //基于CGLB的bean動態代理創建(策略模式) this.instantiationStrategy = new CglibSubclassingInstantiationStrategy(); //獲取方法或構造函數的參數名稱,這里默認通過java反射機制 this.parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); //是否自動嘗試解析bean之間的循環依賴 this.allowCircularReferences = true; //在循環依賴的時候,是否可以注入原始的bean,應該是為進行屬性賦值的原始bean this.allowRawInjectionDespiteWrapping = false; //根據類型忽略相關的實現 this.ignoredDependencyTypes = new HashSet(); //忽略相關接口 this.ignoredDependencyInterfaces = new HashSet(); // 當前創建的bean的名稱,用于隱式依賴項注冊 // 從用戶指定的Supplier回調觸發的getBean等調用上 this.currentlyCreatedBean = new NamedThreadLocal("Currently created bean"); //未完成的FactoryBean實例的緩存:BeanWrapper的FactoryBean名稱 this.factoryBeanInstanceCache = new ConcurrentHashMap(); //按工廠類別緩存候選工廠方法 this.factoryMethodCandidateCache = new ConcurrentHashMap(); //過濾后的PropertyDescriptor的緩存:Bean類到PropertyDescriptor數組。 this.filteredPropertyDescriptorsCache = new ConcurrentHashMap(); this.ignoreDependencyInterface(BeanNameAware.class); this.ignoreDependencyInterface(BeanFactoryAware.class); this.ignoreDependencyInterface(BeanClassLoaderAware.class); }
源碼中初始化了bean的代理模式、bean循環依賴處理配置,以及緩存bean的相關工廠信息。 最后忽略了BeanNameAware、BeanFactoryAware 、 BeanClassLoaderAware三個接口的相關類。
暫時原因并不清楚,為何要排除。這里暫時先不說,等說道bean加載的時候在說。
主要幾點:
1.資源類注解@Resouce、@Value等,背后的支持類初始化完成。 2.bean加載相關注解,@Component、@Service,背后的支持類初始化。 3.基于condition的條件注入 4.DefaultListableBeanFactory工廠類的初始化。所謂IOC容器也是在這里進行初始化。且內部涉及到bean的循環依賴配置。
上述就是小編為大家分享的springboot中如何初始化上下文構建類了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。