spring boot提供了sample程序,学习spring boot之前先跑一个最简单的示例:

  1. /*
  2. * Copyright 2012-2016 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16.  
  17. package sample.simple;
  18.  
  19. import sample.simple.ExitException;
  20. import sample.simple.service.HelloWorldService;
  21.  
  22. import org.springframework.beans.factory.annotation.Autowired;
  23. import org.springframework.boot.CommandLineRunner;
  24. import org.springframework.boot.SpringApplication;
  25. import org.springframework.boot.autoconfigure.SpringBootApplication;
  26. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  27.  
  28. @SpringBootApplication
  29. public class SampleSimpleApplication implements CommandLineRunner {
  30.  
  31. // Simple example shows how a command line spring application can execute an
  32. // injected bean service. Also demonstrates how you can use @Value to inject
  33. // command line args ('--name=whatever') or application properties
  34.  
  35. @Autowired
  36. private HelloWorldService helloWorldService;
  37.  
  38. public void run(String... args) {
  39. System.out.println(this.helloWorldService.getHelloMessage());
  40. if (args.length > 0 && args[0].equals("exitcode")) {
  41. throw new ExitException();
  42. }
  43. }
  44.  
  45. public static void main(String[] args) throws Exception {
  46. SpringApplication application = new SpringApplication(
  47. SampleSimpleApplication.class);
  48. application.setApplicationContextClass(AnnotationConfigApplicationContext.class);
  49. SpringApplication.run(SampleSimpleApplication.class, args);
  50. }
  51.  
  52. }

可以发现在主方法main里启动了一个SpringApplication,启动方法是run方法。

SpringApplication用来从java main方法启动一个spring应用,默认的启动步骤如下:

1)创建一个合适的ApplicationContext实例,这个实例取决于classpath。

2)注册一个CommandLinePropertySource,以spring属性的形式来暴露命令行参数。

3)刷新ApplicationContext,加载所有的单例bean。

4)触发所有的命令行CommanLineRunner来执行bean。

大部分场景下,可以从你的application的main方法中直接调用它的run()静态方法。示例如下:

  1. @Configuration
  2. @EnableAutoConfiguration
  3. public class MyApplication {
  4.  
  5. // ... Bean definitions
  6.  
  7. public static void main(String[] args) throws Exception {
  8. SpringApplication.run(MyApplication.class, args);
  9. }

定制则可以这样:

  1. public static void main(String[] args) throws Exception {
  2. SpringApplication app = new SpringApplication(MyApplication.class);
  3. // ... customize app settings here
  4. app.run(args)
  5. }

springApplication可以读取不同种类的源文件:

  • 类- java类由AnnotatedBeanDefinitionReader加载。
  • Resource - xml资源文件由XmlBeanDefinitionReader读取, 或者groovy脚本由GroovyBeanDefinitionReader读取
  • Package - java包文件由ClassPathBeanDefinitionScanner扫描读取。
  • CharSequence - 字符序列可以是类名、资源文件、包名,根据不同方式加载。如果一个字符序列不可以解析程序到类,也不可以解析到资源文件,那么就认为它是一个包。

1.初始化过程

  1. public SpringApplication(Object... sources) {
  2. initialize(sources);
  3. }
  4. private void initialize(Object[] sources) {
  5. if (sources != null && sources.length > 0) {
  6. this.sources.addAll(Arrays.asList(sources));
  7. }
  8. this.webEnvironment = deduceWebEnvironment();
  9. setInitializers((Collection) getSpringFactoriesInstances(
  10. ApplicationContextInitializer.class));
  11. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  12. this.mainApplicationClass = deduceMainApplicationClass();
  13. }

2.运行方法run

  1. /**
  2. * Run the Spring application, creating and refreshing a new
  3. * {@link ApplicationContext}.
  4. * @param args the application arguments (usually passed from a Java main method)
  5. * @return a running {@link ApplicationContext}
  6. */
  7. public ConfigurableApplicationContext run(String... args) {
  8. StopWatch stopWatch = new StopWatch();
  9. stopWatch.start();
  10. ConfigurableApplicationContext context = null;
  11. configureHeadlessProperty();
  12. SpringApplicationRunListeners listeners = getRunListeners(args);
  13. listeners.started();
  14. try {
  15. ApplicationArguments applicationArguments = new DefaultApplicationArguments(
  16. args);
  17. context = createAndRefreshContext(listeners, applicationArguments);
  18. afterRefresh(context, applicationArguments);
  19. listeners.finished(context, null);
  20. stopWatch.stop();
  21. if (this.logStartupInfo) {
  22. new StartupInfoLogger(this.mainApplicationClass)
  23. .logStarted(getApplicationLog(), stopWatch);
  24. }
  25. return context;
  26. }
  27. catch (Throwable ex) {
  28. handleRunFailure(context, listeners, ex);
  29. throw new IllegalStateException(ex);
  30. }
  31. }

2.1 配置属性

  1. private void configureHeadlessProperty() {
  2. System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
  3. SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
  4. }

2.2 获取监听器

  1. private SpringApplicationRunListeners getRunListeners(String[] args) {
  2. Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
  3. return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
  4. SpringApplicationRunListener.class, types, this, args));
  5. }

2.3 启动监听器

  1. public void started() {
  2. for (SpringApplicationRunListener listener : this.listeners) {
  3. listener.started();
  4. }
  5. }

listener最终会被初始化为ParentContextCloserApplicationListener,FileEncodingApplicationListener,AnsiOutputApplicationListener,ConfigFileApplicationListener,DelegatingApplicationListener,LiquibaseServiceLocatorApplicationListener,ClasspathLoggingApplicationListener,LoggingApplicationListener这几个类的对象组成的list。

下图画出了加载的ApplicationListener,并说明了他们的作用。

2.4 创建并刷新容器(重点)

  1. private ConfigurableApplicationContext createAndRefreshContext(
  2. SpringApplicationRunListeners listeners,
  3. ApplicationArguments applicationArguments) {
  4. ConfigurableApplicationContext context;
  5. // Create and configure the environment
  6. ConfigurableEnvironment environment = getOrCreateEnvironment();
  7. configureEnvironment(environment, applicationArguments.getSourceArgs());
  8. listeners.environmentPrepared(environment);
  9. if (isWebEnvironment(environment) && !this.webEnvironment) {
  10. environment = convertToStandardEnvironment(environment);
  11. }
  12.  
  13. if (this.bannerMode != Banner.Mode.OFF) {
  14. printBanner(environment);
  15. }
  16.  
  17. // Create, load, refresh and run the ApplicationContext
  18. context = createApplicationContext();
  19. context.setEnvironment(environment);
  20. postProcessApplicationContext(context);
  21. applyInitializers(context);
  22. listeners.contextPrepared(context);
  23. if (this.logStartupInfo) {
  24. logStartupInfo(context.getParent() == null);
  25. logStartupProfileInfo(context);
  26. }
  27.  
  28. // Add boot specific singleton beans
  29. context.getBeanFactory().registerSingleton("springApplicationArguments",
  30. applicationArguments);
  31.  
  32. // Load the sources
  33. Set<Object> sources = getSources();
  34. Assert.notEmpty(sources, "Sources must not be empty");
  35. load(context, sources.toArray(new Object[sources.size()]));
  36. listeners.contextLoaded(context);
  37.  
  38. // Refresh the context
  39. refresh(context);
  40. if (this.registerShutdownHook) {
  41. try {
  42. context.registerShutdownHook();
  43. }
  44. catch (AccessControlException ex) {
  45. // Not allowed in some environments.
  46. }
  47. }
  48. return context;
  49. }

2.4.1 获取或者创建环境

  1. private ConfigurableEnvironment getOrCreateEnvironment() {
  2. if (this.environment != null) {
  3. return this.environment;
  4. }
  5. if (this.webEnvironment) {
  6. return new StandardServletEnvironment();
  7. }
  8. return new StandardEnvironment();
  9. }

若是有指定环境,则返回指定的ConfigurableEnvironment

  1. public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver

使用示例如下:

以最高搜索级别增加一个属性

  1. ConfigurableEnvironment environment = new StandardEnvironment();
  2. MutablePropertySources propertySources = environment.getPropertySources();
  3. Map myMap = new HashMap();
  4. myMap.put("xyz", "myValue");
  5. propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));

移除默认系统属性

  1. MutablePropertySources propertySources = environment.getPropertySources();
  2. propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)

测试环境mock系统属性

  1. MutablePropertySources propertySources = environment.getPropertySources();
  2. MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
  3. propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);

若是web环境,则使用StandardServletEnvironment

  1. public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment

它用于基于server相关的web应用,所有web相关(Servlet相关)Application类默认初始化一个实例。

默认返回StandardEnvironment

  1. public class StandardEnvironment extends AbstractEnvironment

StandardEnvironment例如非web环境等

2.4.2 配置环境

  1. /**
  2. * Template method delegating to
  3. * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
  4. * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
  5. * Override this method for complete control over Environment customization, or one of
  6. * the above for fine-grained control over property sources or profiles, respectively.
  7. * @param environment this application's environment
  8. * @param args arguments passed to the {@code run} method
  9. * @see #configureProfiles(ConfigurableEnvironment, String[])
  10. * @see #configurePropertySources(ConfigurableEnvironment, String[])
  11. */
  12. protected void configureEnvironment(ConfigurableEnvironment environment,
  13. String[] args) {
  14. configurePropertySources(environment, args);
  15. configureProfiles(environment, args);
  16. }

2.4.3 创建ApplicationContext

  1. /**
  2. * Strategy method used to create the {@link ApplicationContext}. By default this
  3. * method will respect any explicitly set application context or application context
  4. * class before falling back to a suitable default.
  5. * @return the application context (not yet refreshed)
  6. * @see #setApplicationContextClass(Class)
  7. */
  8. protected ConfigurableApplicationContext createApplicationContext() {
  9. Class<?> contextClass = this.applicationContextClass;
  10. if (contextClass == null) {
  11. try {
  12. contextClass = Class.forName(this.webEnvironment
  13. ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
  14. }
  15. catch (ClassNotFoundException ex) {
  16. throw new IllegalStateException(
  17. "Unable create a default ApplicationContext, "
  18. + "please specify an ApplicationContextClass",
  19. ex);
  20. }
  21. }
  22. return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
  23. }

2.4.4 加载bean到ApplicationContext

  1. /**
  2. * Load beans into the application context.
  3. * @param context the context to load beans into
  4. * @param sources the sources to load
  5. */
  6. protected void load(ApplicationContext context, Object[] sources) {
  7. if (logger.isDebugEnabled()) {
  8. logger.debug(
  9. "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
  10. }
  11. BeanDefinitionLoader loader = createBeanDefinitionLoader(
  12. getBeanDefinitionRegistry(context), sources);
  13. if (this.beanNameGenerator != null) {
  14. loader.setBeanNameGenerator(this.beanNameGenerator);
  15. }
  16. if (this.resourceLoader != null) {
  17. loader.setResourceLoader(this.resourceLoader);
  18. }
  19. if (this.environment != null) {
  20. loader.setEnvironment(this.environment);
  21. }
  22. loader.load();
  23. }

2.4.5 刷新ApplicationContext

  1. /**
  2. * Refresh the underlying {@link ApplicationContext}.
  3. * @param applicationContext the application context to refresh
  4. */
  5. protected void refresh(ApplicationContext applicationContext) {
  6. Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
  7. ((AbstractApplicationContext) applicationContext).refresh();
  8. }

小结:

上面仅仅是入门,若有谬误,请指正。后面随着学习的深入会修改。

参考文献:

【1】http://www.cnblogs.com/java-zhao/p/5540309.html

【2】http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow

spring boot源码分析之SpringApplication的更多相关文章

  1. 精尽Spring Boot源码分析 - SpringApplication 启动类的启动过程

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  2. Spring Boot源码分析-配置文件加载原理

    在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读.今天让我们继续阅读源码,了解配置文 ...

  3. Spring Boot源码分析-启动过程

    Spring Boot作为目前最流行的Java开发框架,秉承"约定优于配置"原则,大大简化了Spring MVC繁琐的XML文件配置,基本实现零配置启动项目. 本文基于Spring ...

  4. 精尽Spring Boot源码分析 - 序言

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  5. 精尽Spring Boot源码分析 - 文章导读

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  6. 精尽Spring Boot源码分析 - 内嵌Tomcat容器的实现

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  7. 精尽Spring Boot源码分析 - 支持外部 Tomcat 容器的实现

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  8. 精尽Spring Boot源码分析 - 配置加载

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  9. 精尽Spring Boot源码分析 - 日志系统

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

随机推荐

  1. Codeforces Round #384 (Div. 2)D-Chloe and pleasant prizes

    D. Chloe and pleasant prizes time limit per test 2 seconds memory limit per test 256 megabytes input ...

  2. python 中使用 global 引发了莫名其妙的问题

    哪里出问题了 python 中,使用 global 会将全局变量设为本函数可用.同时,在函数内部访问变量会先本地再全局. 在嵌套函数中,使用 global 会产生不合常理的行为. 上代码: In [9 ...

  3. [转]定位占用oracle数据库cpu过高的sql

    今天在吃饭的时候我的朋友的数据库出现了问题,cpu占用率为97%,当我看到这个问题的时候我就想到了或许是sql导致的此问题,由于忍不住吃饭,暂时没有帮他看这个问题,这是我饭后自己模拟的故障,进行的分析 ...

  4. JDBC的连接和增删改和查找

    package Test2;import java.sql.*;import java.sql.DriverManager;import java.sql.SQLException;public cl ...

  5. 谢欣伦 - OpenDev原创教程 - 媒体开发库libMedia

    libMedia是一个免费的简单的媒体开发库,其中的接口类与函数大都以小写的x打头,来源于我的姓氏首字母(谢欣伦). 下载 OpenDev for VS2012 libMedia提供四大功能,一是视频 ...

  6. C#:获取设备电量相关信息

    更多资源:http://denghejun.github.io [DllImport("kernel32.dll",EntryPoint="GetSystemPowerS ...

  7. C#基础_单例模式

    控制某个类型的实例数量-唯一一个 class Program { static void Main(string[] args) { test t1 = test.GetInstance(); tes ...

  8. ios获取左右眼图片景深图

    cv::Mat leftMat,rightMat,depthMapMat; UIImageToMat(leftImage, leftMat); UIImageToMat(rightImage, rig ...

  9. ABP理论学习之Javascript API(理论完结篇)

    返回总目录 本篇目录 Ajax Notification Message UI block和busy 事件总线 Logging 其他工具功能 说在前面的话 不知不觉,我们送走了2015,同时迎来了20 ...

  10. 老司机学新平台 - Xamarin开发之我的第一个MvvmCross跨平台插件:SimpleAudioPlayer

    大家好,老司机学Xamarin系列又来啦!上一篇MvvmCross插件精选文末提到,Xamarin平台下,一直没找到一个可用的跨平台AudioPlayer插件.那就自力更生,让我们就自己来写一个吧! ...