採用ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
方式構建Spring容器並查看其內部運行過程.java
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.3.RELEASE</version> </dependency>
public class User { private String username; private String password; public User(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="user" class="com.jimisun.learnspringboot.web.User"> <constructor-arg index="0" value="jimisun"/> <constructor-arg index="1" value="jimisun"/> </bean> </beans>
public class Main { public static void main(String[] args) { // 用咱們的配置文件來啓動一個 ApplicationContext ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml"); System.out.println("context 啓動成功"); User user = context.getBean(User.class); System.out.println(user.toString()); } }
ApplicationContext 啓動過程當中,會建立SPring Bean容器,而後初始化相關Bean,再向Bean中注入其相關依賴。web
因此咱們僅僅須要Debug跟蹤Main方法中ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
這一句代碼,查看Spring是如何建立ApplicationContext容器並將xml中的配置信息裝配進容器的.spring
功能:設置此應用程序上下文的配置文件位置,若是未設置;Spring能夠根據須要使用默認值數組
setConfigLocations(configLocations);緩存
在Main方法Debug啓動進入斷點,按F7跟進入其方法查看,會進入ClassPathXmlApplicationContext
類的構造方法中springboot
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { private Resource[] configResources; // 若是已經有 ApplicationContext 並須要配置成父子關係,那麼調用這個構造方法 public ClassPathXmlApplicationContext(ApplicationContext parent) { super(parent); } ... public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); // 根據提供的路徑,處理成配置文件數組(以分號、逗號、空格、tab、換行符分割) setConfigLocations(configLocations); if (refresh) { refresh(); // 核心方法 剩餘的全部步驟都在此方法中!!! } } ... }
首先執行"設置配置位置setConfigLocations"
的方法,解析SpringXML配置文件
地址存儲到configLocations
屬性中。架構
public void setConfigLocations(@Nullable String... locations) { //判斷配置路徑是否爲null if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); //循環將配置文件路徑存儲到屬性configLocations中 this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
注意:除了第一步設置XML配置文件路徑,剩餘的步驟都在該類的refresh();這個方法中執行,因此咱們須要Debug跟進入這個方法app
refresh();方法以下所示;由於整個SpringApplication的構建都在這個方法 裏面因此就如今這裏展示一下和你們混個臉熟.分佈式
public void refresh() throws BeansException, IllegalStateException { //對下面的代碼塊添加同步鎖 synchronized (this.startupShutdownMonitor) { //第二步: 執行建立容器前的準備工做 :記錄下容器的啓動時間、標記「已啓動」狀態、處理配置文件中的佔位符 prepareRefresh(); //第三步:建立Bean容器,加載XML配置信息 : 若是存在容器進行銷燬舊容器,建立新容器,解析XML配置文件爲一個個BeanDefinition定義註冊到新容器(BeanFactory)中,注意Bean未初始化 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //第四步: 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動註冊幾個特殊的 bean prepareBeanFactory(beanFactory); try { //第五步:加載並執行後置處理器 postProcessBeanFactory(beanFactory); //執行postProcessBeanFactory()方法 invokeBeanFactoryPostProcessors(beanFactory); // 實例化攔截Bean建立的後置處理器beanPostProcessors registerBeanPostProcessors(beanFactory); //第六步: 初始化Spring容器的消息源 initMessageSource(); //第七步:初始化Spring容器事件廣播器 initApplicationEventMulticaster(); // 空方法 onRefresh(); //第八步:註冊事件監聽器 registerListeners(); //第九步核心方法:初始化(構造)全部在XML文件中配置的單例非延遲加載的bean finishBeanFactoryInitialization(beanFactory); //第十步:清理緩存,若是容器中存Bean名爲lifecycleProcessor的Bean 對其進行註冊,若是不存在建立一個DefaultLifecycleProcessor進行註冊 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 摧毀已經建立的單身人士以免懸空資源。 destroyBeans(); // 重置'有效'標誌。 cancelRefresh(ex); // 向調用者傳播異常。 throw ex; } finally { //重置Spring核心的工具類的緩存 resetCommonCaches(); } } }
第二步的主要工做:準備工做,記錄下容器的啓動時間、標記「已啓動」狀態、處理配置文件中的佔位符,初始化事件屬性。
prepareRefresh();
protected void prepareRefresh() { // 記錄啓動時間, // 將 active 屬性設置爲 true,closed 屬性設置爲 false,它們都是 AtomicBoolean類型 this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); //打印Logger if (logger.isDebugEnabled()) { if (logger.isTraceEnabled()) { logger.trace("Refreshing " + this); } else { logger.debug("Refreshing " + getDisplayName()); } } // 在上下文環境中初始化任何佔位符屬性源 空方法 默認狀況下不執行任何操做。 initPropertySources(); // 校驗 xml 配置文件 getEnvironment().validateRequiredProperties(); this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); }
主要工做:進行銷燬舊容器,建立新容器,加載BeanDefinition到BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
@Override protected final void refreshBeanFactory() throws BeansException { // 若是ApplicationContext中已經的BeanFactory屬性已經有值,銷燬此BeanFactory全部 Bean,關閉 BeanFactory,從新建立一個新的Bean容器設置給ApplicationContext的beanFactory屬性 if (hasBeanFactory()) { //銷燬容器 destroyBeans(); //建立類型爲DefaultListableBeanFactory新容器放入BeanFactory變量中 closeBeanFactory(); } try { //建立類型爲DefaultListableBeanFactory新容器放入BeanFactory變量中 DefaultListableBeanFactory beanFactory = createBeanFactory(); //設置BeanFactory的序列化ID也就是其類名 beanFactory.setSerializationId(getId()); // 設置 BeanFactory 的兩個配置屬性:是否容許 Bean 覆蓋、是否容許循環引用 customizeBeanFactory(beanFactory); //這個方法將根據配置,加載各個Bean,而後放到 BeanFactory 中 注意:這裏的加載並非初始化這個Bean 而是以Key-value的形式存儲在beanFactory; beanName-> beanDefinition 的 map loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
主要工做:在Bean容器建立完畢會"手動"註冊一些特殊的 bean。官網這樣解釋: " 配置工廠的標準上下文特徵,例如上下文的ClassLoader和後處理器 "。
具體方法 : prepareBeanFactory(factory) ;
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 這裏設置爲加載當前 ApplicationContext 類的類加載器 beanFactory.setBeanClassLoader(getClassLoader()); // 設置 Bean的表達式解析器 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); //默認添加一個ApplicationContextAwareProcessor的BeanPostProcessor,實現了ApplicationContextAware接口的Bean,Spring會將上下文ApplicationContext注入Bean屬性中 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 下面幾行的意思就是,若是某個 bean 依賴於如下幾個接口的實現類,在自動裝配的時候忽略它們, beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); /** * 下面幾行就是爲特殊的幾個 bean 賦值,若是有 bean 依賴瞭如下幾個,會注入這邊相應的值, * 以前咱們說過,"當前 ApplicationContext 持有一個 BeanFactory",這裏解釋了第一行 * ApplicationContext 還繼承了 ResourceLoader、ApplicationEventPublisher、MessageSource * 因此對於這幾個依賴,能夠賦值爲 this,注意 this 是一個 ApplicationContext * 那這裏怎麼沒看到爲 MessageSource 賦值呢?那是由於 MessageSource 被註冊成爲了一個普通的 bean */ beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); //註冊早期後處理器以檢測內部bean做爲ApplicationListeners beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // 若是檢測到LoadTimeWeaver 準備編織 不是咱們本章的重點無需關注 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } //默認註冊 environment systemEnvironment systemProperties的Bean 咱們能夠選擇覆蓋 if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } } ```
主要功能:實例化在XML配置中實現了BeanFactoryPostProcessor和BeanPostProcessors接口的Bean並執行其回調方法.注意:此時普通的Bean仍然並無初始化
//實例化並調用XML配置中實現了BeanFactoryPostProcessors接口的的回調 postProcessBeanFactory(beanFactory); invokeBeanFactoryPostProcessors(beanFactory); //實例化並調用XML配置中實現了BeanPostProcessors接口的的回調 registerBeanPostProcessors(beanFactory);
//註解:這裏在建立完成Bean容器後執行BeanFactoryPostProcessors接口的回調,咱們能夠在Bean容器初始化完成的時候完成咱們本身的業務邏輯(不多用),而後是registerBeanPostProcessors(beanFactory)方法,此方法的官方解釋是:"Register bean processors that intercept bean creation(若是存在則註冊攔截bean建立的bean後置處理器)"
主要功能: 初始化MessageSource。若是在此上下文中未定義,則使用parent。
// 初始化ApplicationContext的消息源。 initMessageSource();
protected void initMessageSource() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); //判斷beanFactory中是否有messageSource的Bean if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); // 使MessageSource知道父MessageSource if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) { HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource; if (hms.getParentMessageSource() == null) { //若是沒有父MessageSource,則此消息源設置爲父MessageSource hms.setParentMessageSource(getInternalParentMessageSource()); } } if (logger.isTraceEnabled()) { logger.trace("Using MessageSource [" + this.messageSource + "]"); } } else { // 若是沒有則建立一個默認的DelegatingMessageSource消息源 DelegatingMessageSource dms = new DelegatingMessageSource(); dms.setParentMessageSource(getInternalParentMessageSource()); this.messageSource = dms; beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource); if (logger.isTraceEnabled()) { logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]"); } } }
PS : 在實際項目中咱們不多會用到Spring的事件廣播器,由於如今都是分佈式應用了局部通信不多使用了 一篇很棒的關於Spring容器的事件講解 https://juejin.im/post/5a543ceb518825734a748c44
主要功能 : 註冊Spring的事件廣播器用於廣播Spring的內置事件和自定義事件
initApplicationEventMulticaster();
protected void initApplicationEventMulticaster() { //初始化ApplicationEventMulticaster ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); if (logger.isTraceEnabled()) { logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); } } //若是在上下文中沒有定義,則建立一個默認的SimpleApplicationEventMulticaster。 else { this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); if (logger.isTraceEnabled()) { logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " + "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]"); } } }
主要功能 : 實例化實現ApplicationListener接口的bean。
// 註冊監聽器 finishBeanFactoryInitialization(beanFactory);
protected void registerListeners() { //首先註冊靜態指定的偵聽器 for (ApplicationListener<?> listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } //下面是咱們自定義的監聽器,Spring文檔中給出的建議是 "不要在這裏初始化FactoryBeans:咱們須要保留全部常規bean" String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } //使用已經註冊的事件廣播器,發佈早期的應用程序事件...... Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
功能:執行到這一步,Spring.xml配置文件中的特殊的Bean該註冊的也註冊了,該調用的也調用了,就剩下了普通的Bean了,在這一步就都實例化了.(僅僅是非延遲實例化的單例Bean),也就是說這一步就已經完成了Bean工廠(ApplicationContext)的初始化了.
// 實例化全部SPring.xml配置文件中配置的非延遲實例化的單例Bean finishBeanFactoryInitialization(beanFactory);
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // 初始化此上下文的轉換服務 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } //若是沒有bean後處理器,則註冊默認的嵌入值解析器(例如PropertyPlaceholderConfigurer bean)以前註冊過;此時,主要用於註釋屬性值的分辨率。 if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); } // 儘早初始化LoadTimeWeaverAware bean以容許儘早註冊其變換器。 String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // 中止使用臨時ClassLoader進行類型匹配。 beanFactory.setTempClassLoader(null); // 容許緩存全部bean定義元數據,而不指望進一步的更改。 beanFactory.freezeConfiguration(); // 實例化全部剩餘(非延遲初始化)單例。 beanFactory.preInstantiateSingletons(); }
功能:進行相關的容器建立完成時的操做,回收相關資源
finishRefresh(); resetCommonCaches();
protected void finishRefresh() { //清除上下文級資源緩存(例如來自掃描的ASM元數據)。 clearResourceCaches(); //爲此上下文初始化生命週期處理器。 initLifecycleProcessor(); // 首先將刷新傳播到生命週期處理器。 getLifecycleProcessor().onRefresh(); // 廣播最終事件 publishEvent(new ContextRefreshedEvent(this)); // 若是處於活動狀態,請參與LiveBeansView LiveBeansView.registerApplicationContext(this); }
//清除一些單例的工具類的緩存 protected void resetCommonCaches() { ReflectionUtils.clearCache(); AnnotationUtils.clearCache(); ResolvableType.clearCache(); CachedIntrospectionResults.clearClassLoader(getClassLoader()); }
能夠看到Bean容器中的Bean定義映射關係的Map中存放的是key(String)
->GenericBeanDefinition
的映射,那麼GenericBeanDefinition
又是什麼呢?
BeanDefinition
中保存了咱們的 Bean 信息
,好比這個 Bean 指向的是哪一個類、是不是單例的、是否懶加載、這個 Bean 依賴了哪些 Bean 等等。
經過Debug
的過程當中咱們能夠看到咱們使用ClassPathXmlApplicationContext
構造的ApplicationContext
對象其實在內部維護了一個屬性名爲beanFactory
,咱們的SpringBean都被定義在這個屬性裏面,也就是說beanFactory
這個屬性纔是容器,ApplicationContext
僅僅是作了一層包裝.那麼beanFactory
又是什麼呢?
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { ... }
能夠看到DefaultListableBeanFactory
類也是Bean容器,並且是繼承了全部其餘的容器的功能,能夠說是最爲強大的容器;例如具備(分層,獲取多個容器,注入功能....)
第一次參閱源碼寫的比較慎重,其中因爲身體抱恙又有所當誤,因此在發佈本章的時候也是幾天後了,總的來講本章並無什麼重點,僅僅是把Spring的IOC容器的啓動過程進行了標註,並未作過多底層的深度剖析,例如loadBeanDefinitions(beanFactory)Spring如何將XMl文件的配置裝載入Bean工廠
,以及後面的每一個註釋均可以新開一篇長篇大論的文章,後面儘量的在Spring Framework深度剖析專欄中更爲詳細的學習Spring總體架構源碼
該教程所屬Java工程師之Spring Framework深度剖析專欄,本系列相關博文目錄 Java工程師之Spring Framework深度剖析專欄