javascript
spring boot源码分析之SpringApplication
spring boot提供了sample程序,學習spring boot之前先跑一個最簡單的示例:
/** Copyright 2012-2016 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package sample.simple;import sample.simple.ExitException; import sample.simple.service.HelloWorldService;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.AnnotationConfigApplicationContext;@SpringBootApplication public class SampleSimpleApplication implements CommandLineRunner {// Simple example shows how a command line spring application can execute an// injected bean service. Also demonstrates how you can use @Value to inject// command line args ('--name=whatever') or application properties @Autowiredprivate HelloWorldService helloWorldService;public void run(String... args) {System.out.println(this.helloWorldService.getHelloMessage());if (args.length > 0 && args[0].equals("exitcode")) {throw new ExitException();}}public static void main(String[] args) throws Exception {SpringApplication application = new SpringApplication(SampleSimpleApplication.class);application.setApplicationContextClass(AnnotationConfigApplicationContext.class);SpringApplication.run(SampleSimpleApplication.class, args);}}可以發現在主方法main里啟動了一個SpringApplication,啟動方法是run方法。
SpringApplication用來從java main方法啟動一個spring應用,默認的啟動步驟如下:
1)創建一個合適的ApplicationContext實例,這個實例取決于classpath。
2)注冊一個CommandLinePropertySource,以spring屬性的形式來暴露命令行參數。
3)刷新ApplicationContext,加載所有的單例bean。
4)觸發所有的命令行CommanLineRunner來執行bean。
大部分場景下,可以從你的application的main方法中直接調用它的run()靜態方法。示例如下:
@Configuration@EnableAutoConfigurationpublic class MyApplication {// ... Bean definitionspublic static void main(String[] args) throws Exception {SpringApplication.run(MyApplication.class, args);}定制則可以這樣:
public static void main(String[] args) throws Exception {SpringApplication app = new SpringApplication(MyApplication.class);// ... customize app settings here app.run(args)}springApplication可以讀取不同種類的源文件:
- 類- java類由AnnotatedBeanDefinitionReader加載。
- Resource?- xml資源文件由XmlBeanDefinitionReader讀取, 或者groovy腳本由GroovyBeanDefinitionReader讀取
- Package?- java包文件由ClassPathBeanDefinitionScanner掃描讀取。
- CharSequence?- 字符序列可以是類名、資源文件、包名,根據不同方式加載。如果一個字符序列不可以解析程序到類,也不可以解析到資源文件,那么就認為它是一個包。
1.初始化過程
public SpringApplication(Object... sources) {initialize(sources);}private void initialize(Object[] sources) {if (sources != null && sources.length > 0) {this.sources.addAll(Arrays.asList(sources));}this.webEnvironment = deduceWebEnvironment();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}?
2.運行方法run
/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started();try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);context = createAndRefreshContext(listeners, applicationArguments);afterRefresh(context, applicationArguments); listeners.finished(context, null);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}return context;}catch (Throwable ex) {handleRunFailure(context, listeners, ex);throw new IllegalStateException(ex);}}2.1 配置屬性
private void configureHeadlessProperty() {System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));}2.2 獲取監聽器
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}2.3 啟動監聽器
public void started() {for (SpringApplicationRunListener listener : this.listeners) {listener.started();}}listener最終會被初始化為ParentContextCloserApplicationListener,FileEncodingApplicationListener,AnsiOutputApplicationListener,ConfigFileApplicationListener,DelegatingApplicationListener,LiquibaseServiceLocatorApplicationListener,ClasspathLoggingApplicationListener,LoggingApplicationListener這幾個類的對象組成的list。
下圖畫出了加載的ApplicationListener,并說明了他們的作用。
?
?
?
2.4 創建并刷新容器(重點)
private ConfigurableApplicationContext createAndRefreshContext(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {ConfigurableApplicationContext context;// Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());listeners.environmentPrepared(environment);if (isWebEnvironment(environment) && !this.webEnvironment) {environment = convertToStandardEnvironment(environment);}if (this.bannerMode != Banner.Mode.OFF) {printBanner(environment);}// Create, load, refresh and run the ApplicationContextcontext = createApplicationContext();context.setEnvironment(environment);postProcessApplicationContext(context);applyInitializers(context);listeners.contextPrepared(context);if (this.logStartupInfo) {logStartupInfo(context.getParent() == null);logStartupProfileInfo(context);}// Add boot specific singleton beanscontext.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);// Load the sourcesSet<Object> sources = getSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[sources.size()]));listeners.contextLoaded(context);// Refresh the context refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments. }}return context;}2.4.1 獲取或者創建環境
private ConfigurableEnvironment getOrCreateEnvironment() {if (this.environment != null) {return this.environment;}if (this.webEnvironment) {return new StandardServletEnvironment();}return new StandardEnvironment();}若是有指定環境,則返回指定的ConfigurableEnvironment
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver使用示例如下:
以最高搜索級別增加一個屬性
ConfigurableEnvironment environment = new StandardEnvironment();MutablePropertySources propertySources = environment.getPropertySources();Map myMap = new HashMap();myMap.put("xyz", "myValue");propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));移除默認系統屬性
MutablePropertySources propertySources = environment.getPropertySources();propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)測試環境mock系統屬性
MutablePropertySources propertySources = environment.getPropertySources();MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);若是web環境,則使用StandardServletEnvironment
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment它用于基于server相關的web應用,所有web相關(Servlet相關)Application類默認初始化一個實例。
默認返回StandardEnvironment
public class StandardEnvironment extends AbstractEnvironmentStandardEnvironment例如非web環境等
2.4.2 配置環境
/*** Template method delegating to* {@link #configurePropertySources(ConfigurableEnvironment, String[])} and* {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.* Override this method for complete control over Environment customization, or one of* the above for fine-grained control over property sources or profiles, respectively.* @param environment this application's environment* @param args arguments passed to the {@code run} method* @see #configureProfiles(ConfigurableEnvironment, String[])* @see #configurePropertySources(ConfigurableEnvironment, String[])*/protected void configureEnvironment(ConfigurableEnvironment environment,String[] args) {configurePropertySources(environment, args);configureProfiles(environment, args);}2.4.3 創建ApplicationContext
/*** Strategy method used to create the {@link ApplicationContext}. By default this* method will respect any explicitly set application context or application context* class before falling back to a suitable default.* @return the application context (not yet refreshed)* @see #setApplicationContextClass(Class)*/protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {contextClass = Class.forName(this.webEnvironment? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, "+ "please specify an ApplicationContextClass",ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);}2.4.4 加載bean到ApplicationContext
?
/*** Load beans into the application context.* @param context the context to load beans into* @param sources the sources to load*/protected void load(ApplicationContext context, Object[] sources) {if (logger.isDebugEnabled()) {logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));}BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);if (this.beanNameGenerator != null) {loader.setBeanNameGenerator(this.beanNameGenerator);}if (this.resourceLoader != null) {loader.setResourceLoader(this.resourceLoader);}if (this.environment != null) {loader.setEnvironment(this.environment);}loader.load();}2.4.5 刷新ApplicationContext
/*** Refresh the underlying {@link ApplicationContext}.* @param applicationContext the application context to refresh*/protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);((AbstractApplicationContext) applicationContext).refresh();}?
小結:
上面僅僅是入門,若有謬誤,請指正。后面隨著學習的深入會修改。
參考文獻:
【1】http://www.cnblogs.com/java-zhao/p/5540309.html
【2】http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow
轉載于:https://www.cnblogs.com/davidwang456/p/5846513.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的spring boot源码分析之SpringApplication的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据库更新记录,但程序查不到新记录问题
- 下一篇: Using Headless Mode