public static void main(String[] args) { //代碼很簡單SpringApplication.run(); SpringApplication.run(ConsumerApp.class, args);}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { //這個里面調用了run() 方法,我們轉到定義 return run(new Class<?>[] { primarySource }, args);}//這個run方法代碼也很簡單,就做了兩件事情//1、new了一個SpringApplication() 這么一個對象//2、執行new出來的SpringApplication()對象的run()方法public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}
上面代碼主要做了兩件事情。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //1、先把主類保存起來 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //2、判斷運行項目的類型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //3、掃描當前路徑下META-INF/spring.factories文件的,加載ApplicationContextInitializer接口實例 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //4、掃描當前路徑下META-INF/spring.factories文件的,加載ApplicationListener接口實例 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass();}
構造方法內會調用枚舉WebApplicationType的deduceFromClasspath方法獲得應用類型并設置當前應用是普通web應用、響應式web應用還是非web應用。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
deduceFromClasspath方法由枚舉WebApplicationType提供,具體實現如下:
static WebApplicationType deduceFromClasspath() { //當classpath下只存在org.springframework.web.reactive.DispatcherHandler, //且不存在org.springframework.web.servlet.DispatcherServlet,也不存在 //org.glassfish.jersey.servlet.ServletContainer則運行環境為reactive,該模式是非阻塞模式 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET;}
推斷的過程中重點調用了ClassUtils.isPresent()方法,用來判斷指定類名的類是否存在,是否可以進行加載。ClassUtils.isPresent()方法源代碼如下:
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) { try { forName(className, classLoader); return true; } catch (IllegalAccessError err) { throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + err.getMessage(), err); } catch (Throwable ex) { // Typically ClassNotFoundException or NoClassDefFoundError... return false; }}
isPresent()方法調用了forName()方法,如果在調用forName()方法的過程中出現異常則返回false,也就是目標類不存在。否則,返回true。
看一下forName()方法的部分代碼:
public static Class<?> forName(String name, @Nullable ClassLoader classLoader) throws ClassNotFoundException, LinkageError { // 此處省略一些非空和基礎類型的判斷邏輯代碼 ClassLoader clToUse = classLoader; if (clToUse == null) { //如果為空則獲取默認classLoader clToUse = getDefaultClassLoader(); } try { // 返回加載戶的Class。 return Class.forName(name, false, clToUse); } catch (ClassNotFoundException ex) { // 如果直接加載類出現異常,則嘗試加載內部類。 int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR); if (lastDotIndex != -1) { // 拼接內部類 String innerClassName = name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1); try { return Class.forName(innerClassName, false, clToUse); } catch (ClassNotFoundException ex2) { // Swallow - let original exception get through } } throw ex; }}
通過以上核心代碼,可得知forName()方法主要做的事情就是獲得類加載器,嘗試直接加載類,如果失敗則嘗試加載該類的內部類,如果依舊失敗,則拋出異常。
因此,整個應用類型的推斷分以下步驟:
在類型推斷的過程中枚舉類WebApplicationType定義了具體去加載哪些類:
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };private static final String WEBMVC_INDICATOR_CLASS = "org.springframework." + "web.servlet.DispatcherServlet";private static final String WEBFLUX_INDICATOR_CLASS = "org." + "springframework.web.reactive.DispatcherHandler";private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
利用SPI機制掃描 META-INF/spring.factories 這個文件,并且加載ApplicationContextInitializer、ApplicationListener 接口實例。
總結:上面就是SpringApplication初始化的代碼,new SpringApplication()沒做啥事情 ,利用SPI機制主要加載了META-INF/spring.factories 下面定義的事件監聽器接口實現類。
public ConfigurableApplicationContext run(String... args) { <!--1、這個是一個計時器,沒什么好說的--> StopWatch stopWatch = new StopWatch(); stopWatch.start(); DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; <!--2、這個也不是重點,就是設置了一些環境變量--> configureHeadlessProperty(); <!--3、獲取事件監聽器SpringApplicationRunListener類型,并且執行starting()方法--> SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); try { <!--4、把參數args封裝成DefaultApplicationArguments,這個了解一下就知道--> ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); <!--5、這個很重要準備環境了,并且把環境跟spring上下文綁定好,并且執行environmentPrepared()方法--> //準備容器環境、這里會加載配置文件。在這個方法里面會調用所有監聽器Listener的onApplicationEvent(event); // 此時有一個與配置文件相關的監聽器就會被加載`ConfigFileApplicationListener` ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); <!--6、判斷一些環境的值,并設置一些環境的值--> configureIgnoreBeanInfo(environment); <!--7、打印banner--> Banner printedBanner = printBanner(environment); <!--8、創建上下文,根據項目類型創建上下文--> context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); <!--9、準備上下文,執行完成后調用contextPrepared()方法,contextLoaded()方法--> prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); <!--10、這個是spring啟動的代碼了,這里就回去里面就回去掃描并且初始化單實列bean了--> //這個refreshContext()加載了bean,還啟動了內置web容器,需要細細的去看看 refreshContext(context); <!--11、啥事情都沒有做--> afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } <!--12、執行ApplicationRunListeners中的started()方法--> listeners.started(context); <!--執行Runner(ApplicationRunner和CommandLineRunner)--> callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context;}
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // 創建和配置環境變量 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment;}
/*** 該方法根據webApplicationType判斷當前項目是什么類型項目*/private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: return new StandardServletEnvironment(); case REACTIVE: return new StandardReactiveWebEnvironment(); default: return new StandardEnvironment(); }}//webApplicationType是在new SpringApplication方法中通過WebApplicationType.deduceFromClasspath()進行賦值
枚舉WebApplicationType中定義了三個應用類型:
這里調用newStandardServletEnvironment()方法。
StandardServletEnvironment繼承了StandardEnvironment方法,StandardEnvironment又繼承了AbstractEnvironment方法;在AbstractEnvironment方法中調用了customizePropertySources方法。
public AbstractEnvironment() { customizePropertySources(this.propertySources);}
customizePropertySources方法會回調StandardServletEnvironment方法中的customizePropertySources方法。
protected void customizePropertySources(MutablePropertySources propertySources) { propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); } //在這里又調用了父類StandardEnvironment的方法 super.customizePropertySources(propertySources);}
到這里為止propertySources里面就加載了servletConfigInitParams、servletContextInitParams、systemProperties、systemEnvironment。
然后回到prepareEnvironment方法中,在listeners.environmentPrepared(bootstrapContext, environment);方法中去進行監聽。
void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); }}
繼續進入environmentPrepared方法,會進入到SpringApplicationRunListener接口,這個接口在run方法中的getRunListeners里面獲取,最終是在sprin.factories里面進行加載實現類EventPublishingRunListener,執行的是EventPublishingRunListener類中的environmentPrepared方法。
public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));}
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } }}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { doInvokeListener(listener, event); }}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } }}
進入ConfigFileApplicationListener實現類中的onApplicationEvent方法。
public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { //在這個方法里面讀取配置文件 onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); }}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); postProcessors.add(this); AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { //進入 postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); }}
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { //進入 addPropertySources(environment, application.getResourceLoader());}
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { RandomValuePropertySource.addToEnvironment(environment); //load方法是讀取配置文件的核心方法 new Loader(environment, resourceLoader).load();}
void load() { FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY, (defaultProperties) -> { this.profiles = new LinkedList<>(); this.processedProfiles = new LinkedList<>(); this.activatedProfiles = false; this.loaded = new LinkedHashMap<>(); initializeProfiles(); while (!this.profiles.isEmpty()) { Profile profile = this.profiles.poll(); if (isDefaultProfile(profile)) { addProfileToEnvironment(profile.getName()); } load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false)); this.processedProfiles.add(profile); } load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true)); addLoadedPropertySources(); applyActiveProfiles(defaultProperties); });}
一起來看下context = createApplicationContext(); 這段代碼,這段代碼主要是根據項目類型創建上下文,并且會注入幾個核心組件類。
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}
public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); //1:會去注入一些spring核心組件 this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this);}
Web類型項目創建上下文對象AnnotationConfigServletWebServerApplicationContext 。這里會把 ConfigurationClassPostProcessor 、AutowiredAnnotationBeanPostProcessor 等一些核心組件加入到Spring容器。
下面一起來看下refreshContext(context) 這個方法,這個方法啟動spring的代碼加載了bean,還啟動了內置web容器。
private void refreshContext(ConfigurableApplicationContext context) { // 轉到定義看看 refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } }}
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); //看看refresh()方法去 ((AbstractApplicationContext) applicationContext).refresh();}
轉到AbstractApplicationContext - >refresh()方法里面發現這是spring容器啟動代碼。
/*** 加載或刷新一個持久化的配置,可能是XML文件、屬性文件或關系數據庫模式。* 由于這是一種啟動方法,如果失敗,應該銷毀已經創建的單例,以避免懸空資源。* 換句話說,在調用該方法之后,要么全部實例化,要么完全不實例化。* @throws 如果bean工廠無法初始化,則拋出 BeansException 異常* @throws 如果已經初始化且不支持多次刷新,則會拋出 IllegalStateException 異常*/@Overridepublic void refresh() throws BeansException, IllegalStateException { //加載或刷新配置前的同步處理 synchronized (this.startupShutdownMonitor) { // 為刷新而準備此上下文 prepareRefresh(); // 告訴子類去刷新內部bean工廠。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 準備好bean工廠,以便在此上下文中使用。 prepareBeanFactory(beanFactory); try { // 允許在上下文子類中對bean工廠進行后置處理。 postProcessBeanFactory(beanFactory); // 調用在上下文中注冊為bean的工廠處理器。 invokeBeanFactoryPostProcessors(beanFactory); // 注冊攔截bean創建的bean處理器。 registerBeanPostProcessors(beanFactory); // 初始化此上下文的 message resource 消息資源。 initMessageSource(); // 為這個上下文初始化事件多路廣播器。 initApplicationEventMulticaster(); // 初始化特定上下文子類中的其他特殊bean。 onRefresh(); // 注冊監聽器(檢查監聽器的bean并注冊它們)。 registerListeners(); // 實例化所有剩余的(非 lazy-init 懶初始化的)單例。 finishBeanFactoryInitialization(beanFactory); // 最后一步: 發布相應的事件。 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // 銷毀已經創建的單例,以避免懸空資源。 destroyBeans(); // 重置 'active' 表示. cancelRefresh(ex); // 將異常傳播給調用者。 throw ex; } finally { // 重置Spring內核中的共用的緩存,因為我們可能再也不需要單例bean的元數據了…… resetCommonCaches(); } }}
本文鏈接:http://www.www897cc.com/showinfo-26-12316-0.html玩轉SpringBoot—啟動源碼及外部化配置
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com