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 @Autowired
private 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
@EnableAutoConfiguration
public class MyApplication { // ... Bean definitions public 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 ApplicationContext
context = createApplicationContext();
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
} // Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments); // Load the sources
Set<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 AbstractEnvironment

StandardEnvironment例如非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

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. STM32之通用定时器

    广大的互联网的大家早上中午晚上..又好..没错了..我又来了..写博客不是定时的..为什么我要提写博客不是定时的呢??聪明的人又猜到我要说什么了吧.有前途.其实我还是第一次听到定时器有通用和高级之分的 ...

  2. Android 怎么退出整个应用程序?

    方法一: 我们在写android应用程序时,经常会遇到想退出当前Acitivity,或者直接退出应用程序.我之前的一般操作是按返回键,或者直接按home键直接返回,其实这两种操作都没有关闭当前应用程序 ...

  3. java发送邮件..转

    使用Java应用程序发送E-mail十分简单,但是首先你应该在你的机器上安装JavaMail API 和Java Activation Framework (JAF) . 你可以在 JavaMail ...

  4. Xamarin的不归路-生成安卓错误2

    错误提示: 解决方案:升级Andr​​oid sdk到Andr​​oid6.0即API23重新编译即可. Andr​​oid版本较低的手机如何使用? 你只需要编译和目标设定为6.0,根据需要设置最低A ...

  5. C\C++中声明与定义的区别

    声明和定义是完全同的概念,声明是告诉编译器"这个函数或者变量可以在哪找到,它的模样像什么".而定义则是告诉编译器,"在这里建立变量或函数",并且为它们分配内存空 ...

  6. Java虚拟机

    虚拟机每次方法的调用和返回都伴随着栈帧的入栈和出栈,而每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用(表明该栈帧执行的是哪个方法),持有这个引用是为了支持方法调用中的动态连接.这些符号引用中 ...

  7. 构建 Android 应用程序一定要绕过的 30 个坑

    原文地址:Building Android Apps - 30 things that experience made me learn the hard way 原文作者:César Ferreir ...

  8. checkbox和文字对齐

    <style type="text/css"> input{vertical-align:middle; margin-top:0;} </style>

  9. 一起来测试天兔Lepus3.8 Beta版本的MSSQL部分

    一起来测试天兔Lepus3.8 Beta版本的MSSQL部分 产品介绍:http://www.lepus.cc/下载地址:http://www.lepus.cc/soft/18手册地址:http:// ...

  10. ASP.NET安全

    ASP.NET 安全 概述 安全在web领域是一个永远都不会过时的话题,今天我们就来看一看一些在开发ASP.NET MVC应用程序时一些值得我们注意的安全问题.本篇主要包括以下几个内容 : 认证 授权 ...