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
- @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的更多相关文章
- 精尽Spring Boot源码分析 - SpringApplication 启动类的启动过程
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- Spring Boot源码分析-配置文件加载原理
在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读.今天让我们继续阅读源码,了解配置文 ...
- Spring Boot源码分析-启动过程
Spring Boot作为目前最流行的Java开发框架,秉承"约定优于配置"原则,大大简化了Spring MVC繁琐的XML文件配置,基本实现零配置启动项目. 本文基于Spring ...
- 精尽Spring Boot源码分析 - 序言
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- 精尽Spring Boot源码分析 - 文章导读
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- 精尽Spring Boot源码分析 - 内嵌Tomcat容器的实现
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- 精尽Spring Boot源码分析 - 支持外部 Tomcat 容器的实现
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- 精尽Spring Boot源码分析 - 配置加载
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
- 精尽Spring Boot源码分析 - 日志系统
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
随机推荐
- 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 ...
- python 中使用 global 引发了莫名其妙的问题
哪里出问题了 python 中,使用 global 会将全局变量设为本函数可用.同时,在函数内部访问变量会先本地再全局. 在嵌套函数中,使用 global 会产生不合常理的行为. 上代码: In [9 ...
- [转]定位占用oracle数据库cpu过高的sql
今天在吃饭的时候我的朋友的数据库出现了问题,cpu占用率为97%,当我看到这个问题的时候我就想到了或许是sql导致的此问题,由于忍不住吃饭,暂时没有帮他看这个问题,这是我饭后自己模拟的故障,进行的分析 ...
- JDBC的连接和增删改和查找
package Test2;import java.sql.*;import java.sql.DriverManager;import java.sql.SQLException;public cl ...
- 谢欣伦 - OpenDev原创教程 - 媒体开发库libMedia
libMedia是一个免费的简单的媒体开发库,其中的接口类与函数大都以小写的x打头,来源于我的姓氏首字母(谢欣伦). 下载 OpenDev for VS2012 libMedia提供四大功能,一是视频 ...
- C#:获取设备电量相关信息
更多资源:http://denghejun.github.io [DllImport("kernel32.dll",EntryPoint="GetSystemPowerS ...
- C#基础_单例模式
控制某个类型的实例数量-唯一一个 class Program { static void Main(string[] args) { test t1 = test.GetInstance(); tes ...
- ios获取左右眼图片景深图
cv::Mat leftMat,rightMat,depthMapMat; UIImageToMat(leftImage, leftMat); UIImageToMat(rightImage, rig ...
- ABP理论学习之Javascript API(理论完结篇)
返回总目录 本篇目录 Ajax Notification Message UI block和busy 事件总线 Logging 其他工具功能 说在前面的话 不知不觉,我们送走了2015,同时迎来了20 ...
- 老司机学新平台 - Xamarin开发之我的第一个MvvmCross跨平台插件:SimpleAudioPlayer
大家好,老司机学Xamarin系列又来啦!上一篇MvvmCross插件精选文末提到,Xamarin平台下,一直没找到一个可用的跨平台AudioPlayer插件.那就自力更生,让我们就自己来写一个吧! ...