【附3】springboot源码解析 - 构建SpringApplication
package com.microservice.framework; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MySpringAplication { public void run(String[] args) { SpringApplication sa = new SpringApplication(MySpringAplication.class); sa.run(args); } }
SpringBoot启动过程:
1、构建SpringApplication对象
2、执行run()
一、构建SpringApplication对象
/** * The application context will load beans from the specified sources */ public SpringApplication(Object... sources) { initialize(sources); }
说明:
- 实例化该类的时候会加载bean到applicationContext中去
- 这里的入参是MySpringApplication.class这样一个Class<com.microservice.framework.MySpringApplication>对象
private final Set<Object> sources = new LinkedHashSet<Object>(); private boolean webEnvironment; private Class<?> mainApplicationClass; 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(); }
步骤:
- 将传入的MySpringApplication.class对象放入Set集合
- 判断是否是web环境
- 创建ApplicationInitializer列表
- 初始化ApplicationListener列表
- 初始化主类mainApplicationClass
1.1、将传入的MySpringApplication.class对象放入Set集合
1.2、判断是否是web环境:
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }
说明:通过在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES这个数组中所包含的所有类(实际上就是2个类),如果存在那么当前程序即是一个Web应用程序,反之则不然。
1.3、创建ApplicationContextInitializer列表
private List<ApplicationContextInitializer<?>> initializers; public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<ApplicationContextInitializer<?>>(); this.initializers.addAll(initializers); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = new ArrayList<T>(names.size()); // Create instances from the names for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getConstructor(parameterTypes); T instance = (T) constructor.newInstance(args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } AnnotationAwareOrderComparator.sort(instances); return instances; }
步骤:
- 调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)来获取所有Spring Factories的名字,(这里是获取了四个ApplicationContextInitializer实现类的全类名,见下边)
- 为每一个Spring Factories根据读取到的名字创建其对象。(这里创建了4个对象)
- 将创建好的对象列表排序并返回。
其中,SpringFactoriesLoader.loadFactoryNames(type, classLoader)如下:
/** * The location to look for factories. * <p>Can be present in multiple JAR files. */ public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; /** * Load the fully qualified class names of factory implementations of the * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given * class loader. */ public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
META-INF/spring-factories
1 # Application Context Initializers 2 org.springframework.context.ApplicationContextInitializer=\ 3 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ 4 org.springframework.boot.context.ContextIdApplicationContextInitializer,\ 5 org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ 6 org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
说明:
- 从所有jar获取所有的META-INF/spring-factories文件。(这里只有spring-boot-1.3.0.RELEASE.jar下有一个)
- 遍历每一个spring-factories文件,并获取其下key为factoryClass.getName()(这里是入参
org.springframework.context.ApplicationContextInitializer)的value(这里有以上四个ApplicationContextInitializer实现类)
以上四个类的作用:
至此,设置ApplicationContextInitialize就完成了。
总结:整个setInitializers实际上就是初始化了SpringApplication的属性List<ApplicationContextInitializer<?>> initializers为一个ArrayList列表,该列表中有四个实例:
- ConfigurationWarningsApplicationContextInitializer的实例
- ContextIdApplicationContextInitializer的实例
- DelegatingApplicationContextInitializer实例
- ServerPortInfoApplicationContextInitializer实例
1.4、初始化ApplicationListener列表
private List<ApplicationListener<?>> listeners; /** * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication * and registered with the {@link ApplicationContext}. * @param listeners the listeners to set */ public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<ApplicationListener<?>>(); this.listeners.addAll(listeners); }
META-INF/spring-factories
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\ org.springframework.boot.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.logging.LoggingApplicationListener
以上八个listener的作用如下:
至此,整个setListeners方法结束,初始化了一个包含以上8个ApplicationListener实例的List集合。
1.5、初始化主类mainApplicationClass
private Class<?> mainApplicationClass; private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
说明:获取main()方法所在的主类Class对象,并赋值给SpringApplication的mainApplicationClass属性。
至此,SpringApplication对象初始化完成了。
总结:整个SpringApplication初始化的过程,就是初始化了
- 一个包含入参MySpringApplication.class的sources的Set<Object>
- 一个当前环境是否是web环境的boolean webEnvironment
- 一个包含4个ApplicationContextInitializer实例的List
- 一个包含8个ApplicationListener实例的List
- 一个main方法所在的主类的Class对象。
注意:
本文基本参照http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow完成,该文的作者已经解析的很好了,我这里再抄一遍,只是为了加深记忆!!!
【附3】springboot源码解析 - 构建SpringApplication的更多相关文章
- 附3 springboot源码解析 - 构建SpringApplication
package com.microservice.framework; import org.springframework.boot.SpringApplication; import org.sp ...
- springboot源码解析 - 构建SpringApplication
1 package com.microservice.framework; 2 3 import org.springframework.boot.SpringApplication; 4 impor ...
- 附4 springboot源码解析-run()
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); / ...
- SpringBoot源码解析:创建SpringApplication对象实例
上篇文章SpringBoot自动装配原理解析中,我们分析了SpringBoot的自动装配原理以及@SpringBootApplication注解的原理,本篇文章则继续基于上篇文章中的main方法来分析 ...
- SpringBoot源码解析系列文章汇总
相信我,你会收藏这篇文章的 本篇文章是这段时间撸出来的SpringBoot源码解析系列文章的汇总,当你使用SpringBoot不仅仅满足于基本使用时.或者出去面试被面试官虐了时.或者说想要深入了解一下 ...
- springboot源码解析-管中窥豹系列之总体结构(一)
一.简介 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之项目类型(二)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之Runner(三)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
- springboot源码解析-管中窥豹系列之Initializer(四)
一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...
随机推荐
- 15款Django开发常用软件包(转)
原文:http://www.iteye.com/news/28697 Django是一款高级的Python Web框架,可以帮助开发者快速创建web应用.我们这里整理了15款Django开发中常用的软 ...
- eclipse导出doc帮助文档字符编码设置
- 十天精通CSS3(6)
属性选择器 在HTML中,通过各种各样的属性可以给元素增加很多附加的信息.例如,通过id属性可以将不同div元素进行区分. 在CSS2中引入了一些属性选择器,而CSS3在CSS2的基础上对属性选择器进 ...
- Comparable和Comparator的使用
1:对象实现Comparable, 那么对象就具有了比较功能 package comparableAndComparator; import java.util.Collections; import ...
- 小P的故事——神奇的换零钱&&人活着系列之平方数
http://acm.sdut.edu.cn/sdutoj/showproblem.php?pid=2777&cid=1219 这题不会,看了别人的代码 #include <iostre ...
- phpmyadmin-配合nginx与php安装
1. 概况 phpMyAdmin是用来在网页端图形化操作MySQL数据库的工具,使用起来非常直观,目前最新版本是4.8.3.在搭建web集群架构时可能有这样的需求,数据库安装在专门的一台机器上,但是希 ...
- 找出numpy array数组的最值及其索引
在list列表中,max(list)可以得到list的最大值,list.index(max(list))可以得到最大值对应的索引 但在numpy中的array没有index方法,取而代之的是where ...
- DNS解析原理和流程
DNS解析原理和流程 DNS解析其实就是将IP地址(202.96.134.133)变成域名(www.xxxxx.com) 网络通讯大部分是基于TCP/IP的,而TCP/IP是基于IP地址的,所 ...
- c++虚函数重写的权限问题
cbase.h: #ifndef CBASE_H #define CBASE_H #include<iostream> using std::cout; using std::endl; ...
- 使用i5ting_toc 预览 markdown 文件
i5ting_toc__tree https://github.com/i5ting/i5ting_ztree_toc 是去哪儿网前段架构师狼叔编写的一个jQuery插件,用于将markdown 转化 ...