javascript
Spring加载流程源码
一、從AbstractApplicationContext的體系說起
- 第一,從類結構設計上看, 圍繞著是否需要Refresh容器衍生出兩個抽象類:
AbstractRefreshableApplicationContext及子類無法做到GenericApplicationContext混合搭配從不同源頭獲取bean的定義信息
- 第二, 從加載的源來看(比如xml,groovy,annotation等), 衍生出眾多類型的ApplicationContext, 典型比如:
FileSystemXmlApplicationContext:從文件系統下的一個或多個xml配置文件中加載上下文定義,也就是說系統盤符中加載xml配置文件。
ClassPathXmlApplicationContext: 從類路徑下的一個或多個xml配置文件中加載上下文定義,適用于xml配置的方式。
AnnotationConfigApplicationContext:從一個或多個基于java的配置類中加載上下文定義,適用于java注解的方式。
ConfigurableApplicationContext: 擴展于 ApplicationContext,它新增加了兩個主要的方法: refresh()和 close(),讓ApplicationContext 具有啟動、刷新和關閉應用上下文的能力。在應用上下文關閉的情況下調用refresh()即可啟動應用上下文,在已經啟動的狀態下,調用refresh()則清除緩存并重新裝載配置信息,而調用close()則可關閉應用上下文。這些接口方法為容器的控制管理帶來了便利,但作為開發者,我們并不需要過多關心這些方法。
XmlWebApplicationContext: 繼承自AbstractRefreshableWebApplicationContext,接受能被XmlBeanDefinitionReader所理解的XML文檔配置。對于根上下文,默認的配置文件路徑是/WEB-INF/applicationContext.xml,對于命名空間為test-servlet的上下文,默認的配置文件路徑是/WEB-INF/test-servlet.xml(就像servlet-name為test的DispatcherServlet實例)。
AnnotationConfigWebApplicationContext: 繼承自AbstractRefreshableWebApplicationContext,接受注解的類作為輸入(特殊的@Configuration注解類,一般的@Component注解類,與JSR-330兼容的javax.inject注解)。允許一個一個的注入,同樣也能使用類路徑掃描。對于web環境,基本上是和AnnotationConfigApplicationContext等價的。使用AnnotatedBeanDefinitionReader來對注解的bean進行處理,使用ClassPathBeanDefinitionScanner來對類路徑下的bean進行掃描
這里可以參考這篇文章
二、本文以ClassPathXmlApplicationContext為例,這種方式包含了多種spring加載bean的方式
//如下:開啟spring應用 public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");User bean = ac.getBean("user",User.class);System.out.println(bean); }從ClassPathXmlApplicationContext的構造方法開始查看
- 那么看一下this()
- 再看一下setParent(parent)
- getEnvironment():創建了ConfigurableEnvironment 對象
從提供的方法中可以看出兩個功能
- PropertyResolver:resolveRequiredPlaceholders(path)
處理占位符的方法:
- PropertyPlaceholderHelper
進入doResolvePlaceholders繼續查看
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {@Overridepublic String resolvePlaceholder(String placeholderName) {return getPropertyAsRawString(placeholderName);}}); }getPropertyAsRawString的具體實現在PropertySourcesPropertyResolver類中
@Override protected String getPropertyAsRawString(String key) {return getProperty(key, String.class, false); }繼續跟蹤helper.replacePlaceholders(),到了PropertyPlaceholderHelper.parseStringValue方法,這里面逐一找出每個占位符去做替換:
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {Assert.notNull(value, "'value' must not be null");return parseStringValue(value, placeholderResolver, new HashSet<String>()); }parseStringValue方法中,找到了占位符后,會調用入參placeholderResolver的resolvePlaceholder(placeholder)方法,也就是上面匿名類的getPropertyAsRawString方法(實際上就是PropertySourcesPropertyResolver.getPropertyAsRawString方法),最終會在PropertySourcesPropertyResolver.getProperty方法中找出所有的屬性來匹配占位符
protected String parseStringValue(String strVal, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {StringBuilder result = new StringBuilder(strVal);int startIndex = strVal.indexOf(this.placeholderPrefix);while (startIndex != -1) {int endIndex = findPlaceholderEndIndex(result, startIndex);if (endIndex != -1) {String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);String originalPlaceholder = placeholder;if (!visitedPlaceholders.add(originalPlaceholder)) {throw new IllegalArgumentException("Circular placeholder reference '" + originalPlaceholder + "' in property definitions");}// Recursive invocation, parsing placeholders contained in the placeholder key.placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);// Now obtain the value for the fully resolved key...String propVal = placeholderResolver.resolvePlaceholder(placeholder);if (propVal == null && this.valueSeparator != null) {int separatorIndex = placeholder.indexOf(this.valueSeparator);if (separatorIndex != -1) {String actualPlaceholder = placeholder.substring(0, separatorIndex);String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);if (propVal == null) {propVal = defaultValue;}}}if (propVal != null) {// Recursive invocation, parsing placeholders contained in the// previously resolved placeholder value.propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);if (logger.isTraceEnabled()) {logger.trace("Resolved placeholder '" + placeholder + "'");}startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());}else if (this.ignoreUnresolvablePlaceholders) {// Proceed with unprocessed value.startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());}else {throw new IllegalArgumentException("Could not resolve placeholder '" +placeholder + "'" + " in string value \"" + strVal + "\"");}visitedPlaceholders.remove(originalPlaceholder);}else {startIndex = -1;}}return result.toString();}總結:
剩下最核心的refresh()方法,單開一章
三、核心refresh()方法
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {//做容器刷新前的準備工作//1.設置容器的啟動時間//2.設置活躍狀態為true//3.設置關閉狀態為false//4.獲取Environment對象,并加載當前系統的屬性值到Environment對象中//5.準備監聽器和事件的集合對象,默認為空的集合prepareRefresh();//創建容器對象:DefaultListableFactory//加載xml配置文件的屬性值到當前工廠中,最重要的就是BeanDefinitionConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//beanFactory的準備工作,對各種屬性進行填充prepareBeanFactory(beanFactory);try {// 允許在上下文子類中對 bean 工廠進行后處理。此處我們一般不做任何擴展工作postProcessBeanFactory(beanFactory);// 調用在上下文中注冊為 beanFactory 處理器。invokeBeanFactoryPostProcessors(beanFactory);//注冊能攔截 bean 創建的 bean 處理器,此處只是注冊功能,真正調用的是getBeanregisterBeanPostProcessors(beanFactory);//為上下文初始化message源,即不同語言的消息體,國際化處理initMessageSource();//為此上下文初始化事件多播器initApplicationEventMulticaster();//留給子類來初始化其他beanonRefresh();//在所有注冊的bean中查找listener bean,注冊到消息廣播器中registerListeners();// 初始化剩下的單實例(非懶加載),多例是在getBean時才初始化finishBeanFactoryInitialization(beanFactory);//完成刷新過程,通知生命周期處理器lifecycleProcessor刷新過程,同時發出ContextRefreshEvent通知別人finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}//銷毀已經創建的單例以避免懸空資源destroyBeans();// Reset 'active' flag.//重置active標志cancelRefresh(ex);throw ex;}finally {//重置 Spring 核心中的常見自省緩存,因為我們可能不再需要單例 bean 的元數據resetCommonCaches();}}}- 步驟:
- prepareRefresh
- obtainFreshBeanFactory
- prepareBeanFactory
- postProcessBeanFactory
- invokeBeanFactoryPostProcessors
- registerBeanPostProcessors
- initMessageSource
- initApplicationEventMulticaster
- onRefresh
- registerListeners
- finishBeanFactoryInitialization
- finishRefresh
- resetCommonCaches()
prepareRefresh()
作用:
obtainFreshBeanFactory()
createBeanFactory----創建bean工廠
進入loadBeanDefinition
走完loadBeanDefinitons后,beanFactory中的beandefinitionMap就不會為空了
然后一路再返回到AbstractApplicationContext
作用:
作用:
postProcessBeanFactory方法是留給子類擴展的,可以在bean實例初始化之前注冊后置處理器(類似prepareBeanFactory方法中的beanFactory.addBeanPostProcessor),空實現且沒有子類覆蓋。
該方法執行BeanFactoryPostProcessor執行器
執行BeanFactoryPostProcessor 執行器
作用:
附表:常用的BeanFactoryPostProcessor
| CachingMetadataReaderFactoryPostProcessor(也實現了接口BeanDefinitionRegistryPostProcessor | |
| ConfigurationWarningsPostProcessor(也實現了接口BeanDefinitionRegistryPostProcessor | 主要作用就是把在注冊BeanDefinition實例過程中產生的告警信息傳給Check接口的實例進行處理,ConfigurationWarningsApplicationContextInitializer中只提供了一個Check的實現 ComponentScanPackageCheck,ConfigurationWarningsApplicationContextInitializer的作用是用來報告Spring容器的一些常見的錯誤配置的 |
| ConfigurationClassPostProcessor (也實現了接口BeanDefinitionRegistryPostProcessor) | 主要功能是參與BeanFactory的建造,主要功能如下: 解析加了@Configuration的配置類 解析@ComponentScan掃描的包 解析@ComponentScans掃描的包 |
| PropertySourceOrderingPostProcessor | Bean工廠結束后對環境里的屬性源進行重排序 -> 把名字叫defaultProperties的屬性源放在最末位 |
注意: BeanDefinitionRegistryPostProcessor 繼承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的優先級,主要用來在常規的 BeanFactoryPostProcessor 激活之前注冊一些 bean 定義。
注冊bean處理器,這里只是注冊功能,真正調用的是getBean方法
registerBeanPostProcessors方法的代碼略多,就不在此貼出來了,簡單的說,就是找出所有的bean的后置處理器(注意,是bean的后置處理器,不是beanFactory的后置處理器,bean后置處理器處理的是bean實例,beanfactory后置處理器處理的是bean的定義),然后將這些bean的后置處理器分為三類:
registerBeanPostProcessors方法執行完畢后,beanFactory中已經保存了有序的bean后置處理器,在bean實例化之后,會依次使用這些后置處理器對bean實例來做對應的處理;
作用:
為上下文初始化message源,即不同語言的消息體,國際化處理
初始化事件監聽多路廣播器
作用:
onRefresh是個空方法,留給子類自己實現的,在實例化bean之前做一些ApplicationContext相關的操作,以子類AbstractRefreshableWebApplicationContext為例,看看它的onRefresh方法
方法名為registerListeners,看名字像是將監聽器注冊在事件廣播器中,但實際情況并非如此,只有一些特殊的監聽器被注冊了,那些在bean配置文件中實現了ApplicationListener接口的類還沒有實例化,所以此處只是將其name保存在廣播器中,將這些監聽器注冊在廣播器的操作是在bean的后置處理器中完成的,那時候bean已經實例化完成了,我們看代碼
作用:
初始化剩下的單實例(非懶加載的),多例在getBean時才初始化
- preInstantiateSingletons方法這個方法里就解決了循環依賴的問題
這里接下來的調用鏈條是:
getBean() -> doGetBean() -> createBean() -> doCreateBean() -> populateBean()//填充屬性 -> initializeBean()//初始化(這里涉及到代理的創建)initializeBean方法中會執行applyBeanPostProcessorsAfterInitialization方法
applyBeanPostProcessorsAfterInitialization具體的代碼如下:
@Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor processor : getBeanPostProcessors()) {Object current = processor.postProcessAfterInitialization(result, beanName);if (current == null) {return result;}result = current;}return result; }邏輯比較簡單,就是遍歷所有實現了BeanPostProcessor接口的類,其中AnnotationAwareAspectJAutoProxyCreator(由EnableAspectJAutoProxy導入的)就是會實現AOP動態代理,然后返回代理對象。所以如果沒有導入@EnableAspectJAutoProxy,自然不會創建代理. 這里也可以看出applicationContext構造的過程,就會創建好代理.而且在創建代理的過程中,如果涉及到循環依賴還是提前創建代理并放入earlyProxyReferences集合,用于表示進行了提前的動態代理.這里可以參考這篇文章.注意,多實例對象并不會提前創建,當兩個多實例對象循環引用且設計代理的創建時,就會報錯
附表:SpringBoot常用的BeanPostProcessor清單
| ApplicationContextAwareProcessor | 功能:bean創建時調用bean所實現的各種Aware接口方法設置相應的屬性 |
| WebApplicationContextServletContextAwareProcessor | 功能:Springboot Servlet Web應用中bean創建時調用bean實現的ServletContextAware或者ServletConfigAware接口為bean設置ServletContext或者ServletConfig屬性引入時機:在ServletWebServerApplicationContex#postProcessBeanFactory中登記到應用上下文 |
| PostProcessorRegistrationDelegate$BeanPostProcessorChecker | TBD |
| ConfigurationPropertiesBindingPostProcessor | 功能: 綁定配置文件中的配置屬性項到配置屬性對象,比如server開頭的配置項設置到配置屬性bean對象ServerProperties上 |
| AnnotationAwareAspectJAutoProxyCreator | 功能: 如果某個bean匹配了某些定義的切面advise或者Spring Advisor,則為這個bean創建AOP代理對象 |
| DataSourceInitializerPostProcessor | 功能: 一旦檢測到數據源DataSource bean被初始化,執行數據源的初始化:創建相應的表格(create schema)和填充相應的數據(init schema) |
| MethodValidationPostProcessor | 默認不添加,需要手動添加。支持方法級別的JSR-303規范。需要在類上加上@Validated注解,以及在方法的參數中加上驗證注解,比如@Max,@Min,@NotEmpty …。 |
| BeanValidationPostProcessor | 默認不添加,需要手動添加。主要提供對JSR-303驗證的支持,內部有個boolean類型的屬性afterInitialization,默認是false。如果是false,在postProcessBeforeInitialization過程中對bean進行驗證,否則在postProcessAfterInitialization過程對bean進行驗證。 |
| PersistenceExceptionTranslationPostProcessor | 它將每個Repository組件bean包裝成一個代理對象并為該代理對象添加一個PersistenceExceptionTranslationAdvisor。該PersistenceExceptionTranslationAdvisor會攔截Repository組件bean的方法調用產生的異常并將其轉換成Spring框架標準異常 |
| WebServerFactoryCustomizerBeanPostProcessor | 這個處理器類會獲取TomcatServletWebServerFactoryCustomizer定制器,并調用customize方法進行定制,這時候工廠類起作用,調用getWebServer方法進行Tomcat屬性配置和引擎設置等等,再創建TomcatWebServer啟動Tomcat容器 |
| ErrorPageRegistrarBeanPostProcessor | 功能: 在ErrorPageRegistry bean創建時初始化前將容器中的所有ErrorPageRegistrar bean注冊進來。 |
| DataSourceInitializedPublisher | 用于發布DataSourceInitializedEvent事件 |
| PersistenceAnnotationBeanPostProcessor | 功能: 識別bean上的持久化注解@PersistenceUnit/@PersistenceContext,并完成相應的屬性EntityManagerFactory/EntityManager注入。 |
| CommonAnnotationBeanPostProcessor | 功能: 對JSR-250 @Resource、@PostConstruct 、@PreDestroy等注解的處理 |
| AutowiredAnnotationBeanPostProcessor | 功能: 對每個bean執行真正的依賴"注入",缺省支持三種自動裝配注解@Autowired,@Value @Inject |
| ApplicationListenerDetector | 功能: 檢測單例ApplicationListener bean將它們注冊到應用上下文的事件多播器上,并在這些bean銷毀之前將它們從事件多播器上移除 |
| RequiredAnnotationBeanPostProcessor | 主要處理@Required注解的實現(@Required注解只能修飾方法) |
| ScheduledAnnotationBeanPostProcessor | 默認不添加,使用@EnableScheduling注解后,會被注冊到Spring容器中。主要使用Spring Scheduling功能對bean中使用了@Scheduled注解的方法進行調度處理。 |
| AsyncAnnotationBeanPostProcessor | 默認不添加,使用@EnableAsync注解后,會被注冊到Spring容器中。AsyncAnnotationBeanPostProcessor內部使用aop處理方法的調用。 |
作用:
最后一個方法是finishRefresh,這是在bean的實例化、初始化等完成后的一些操作
- 完成刷新過程,通知生命周期處理器lifecycleProcessor刷新過程,
- 同時發出ContextRefreshEvent通知別人\
作用:
在spring的核心中重置常見的內省緩存,因為我們可能不再需要singleton bean的元數據了
四、總結
參考文章
總結
以上是生活随笔為你收集整理的Spring加载流程源码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国新能源汽车供应链白皮书2020
- 下一篇: 2020年产业互联网发展报告