spring boot 源码解析2-SpringApplication初始化
2018-06-18 01:05:37来源:未知 阅读 ()
前?
我们?成?个spring boot 项?时,会?带?个启动类. 代码如下:
@SpringBootApplication public class SpringBootAnalysisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootAnalysisApplication.class, args); } }
就是这么简单的代码,构成了spring boot的世界. 那么代码中只有?个@SpringBootApplication 注解 和 调?了SpringApplication#run
?法.那么我们先来解析SpringApplication的run?法.
解析
-
?先调?了org.springframework.boot.SpringApplication#run(Object, String...) ?法.代码如下:
public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); }
接着调?如下代码:
public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }
可以发现 ?先初始化了SpringApplication,然后调?其实例?法:run.
2. 在 SpringApplication 的构造器中,调?了 initialize ?法.
public SpringApplication(Object... sources) { initialize(sources); }
3. SpringApplication#initialize?法代码如下:
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(); }
可以看到做了如下5件事:
1. 如果sources?度?于0的话,加?到SpringApplication的sources中,该sources是?个LinkedHashSet.
2. 调?deduceWebEnvironment?法判断是否是web环境
3. 设置initializers.
4. 设置Listeners.
5. 设置mainApplicationClass.
4. deduceWebEnvironment代码如下:
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; }
可以发现会调?ClassUtils类的isPresent?法,检查classpath中是否存在javax.servlet.Servlet类和
org.springframework.web.context.ConfigurableWebApplicationContext类,如果存在的话,返回true.否则返回false.
5. 在设置Initializers时?先调?getSpringFactoriesInstances?法加载ApplicationContextInitializer.然后直接赋值给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保存names来避免重复元素 Set<String> names = new LinkedHashSet<String>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 根据names来进?实例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 对实例进?排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
该?法逻辑如下:
1. ?先获得ClassLoader.
2. 调?SpringFactoriesLoader#loadFactoryNames进?加载,然后放?到LinkedHashSet进?去重.
3. 调?createSpringFactoriesInstances进?初始化
4. 排序
其中SpringFactoriesLoader#loadFactoryNames代码如下:
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURC E_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(factoryClassN ames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
逻辑如下:
1. 获得factoryClassName,对于当前来说factoryClassName =org.springframework.context.ApplicationContextInitializer.
2. 通过传?的classLoader加载META-INF/spring.factories?件.
3. 通过调?PropertiesLoaderUtils#loadProperties将其转为Properties.
4. 获得factoryClassName对应的值进?返回.
对于当前来说,由于我们只加?了spring-boot-starter-web的依赖,因此会加载如下的配置:
1. 在spring-boot/META-INF/spring.factories中.org.springframework.context.ApplicationContextInitializer值如下:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
2. 在spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
中.org.springframework.context.ApplicationContextInitializer值如下:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
因此会加载6个.
SpringApplication#createSpringFactoriesInstances?法如下:
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<T>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass .getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } return instances; }
逻辑如下:遍历传?的names,也就是之前通过SpringFactoriesLoader加载的类名.通过遍历,依次调?其构造器进?初始化.加?到
instances.然后进?返回.
对于当前场景来说:
ConfigurationWarningsApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer
初始化没有做任何事.
ContextIdApplicationContextInitializer在初始化时.会获得spring boot的应?名.搜索路径如下:
1. spring.application.name
2. vcap.application.name
3. spring.config.name
4. 如果都没有配置的话,返回application.
代码如下:
private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${s pring.config.name:application}}}"; public ContextIdApplicationContextInitializer() { this(NAME_PATTERN); } public ContextIdApplicationContextInitializer(String name) { this.name = name; }
6. 设置SpringApplication#setListeners时,还是同样的套路.调?getSpringFactoriesInstances加载META-INF/spring.factories中配置
的org.springframework.context.ApplicationListener. 对于当前来说.加载的类如下:
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
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
这些类在构造器中都没有做任何事.
7. 调?SpringApplication#deduceMainApplicationClass?法.获得应?的启动类.该?法通过获取当前?法调?栈,找到main函数的
类.代码如下:
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; }
流程图如下:
参考视频教程:Spring Boot源码解析
小程序
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- Spring系列.ApplicationContext接口 2020-06-11
- springboot2配置JavaMelody与springMVC配置JavaMelody 2020-06-11
- 给你一份超详细 Spring Boot 知识清单 2020-06-11
- SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis 2020-06-11
- 掌握SpringBoot-2.3的容器探针:实战篇 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash