免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2708 | 回复: 0
打印 上一主题 下一主题

spring boot 源码解析-SpringApplication初始化 [复制链接]

论坛徽章:
1
15-16赛季CBA联赛之同曦
日期:2017-01-17 18:19:30
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2018-05-17 17:02 |只看该作者 |倒序浏览
本帖最后由 调皮仔 于 2018-05-17 17:04 编辑

前⾔
我们⽣成⼀个spring boot项⽬时,会⾃带⼀个启动类. 代码如下:
  1. @SpringBootApplication
  2. public class SpringBootAnalysisApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(SpringBootAnalysisApplication.class, args);
  5. }
  6. }
复制代码
就是这么简单的代码,构成了spring boot的世界. 那么代码中只有⼀个@SpringBootApplication 注解 和 调⽤了SpringApplication#run
⽅法.那么我们先来解析SpringApplication的run⽅法.

解析
  • ⾸先调⽤了org.springframework.boot.SpringApplication#run(Object, String...) ⽅法.代码如下:
    1. public static ConfigurableApplicationContext run(Object source, String... args) {
    2. return run(new Object[] { source }, args);
    3. }
    复制代码
    接着调⽤如下代码:
    1. public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    2. return new SpringApplication(sources).run(args);
    3. }
    复制代码
    可以发现 ⾸先初始化了SpringApplication,然后调⽤其实例⽅法:run.

    2. 在 SpringApplication 的构造器中,调⽤了 initialize ⽅法.
    1. public SpringApplication(Object... sources) {
    2. initialize(sources);
    3. }
    复制代码
    3. SpringApplication#initialize⽅法代码如下:
    1. private void initialize(Object[] sources) {
    2. if (sources != null && sources.length > 0) {
    3. this.sources.addAll(Arrays.asList(sources));
    4. }
    5. this.webEnvironment = deduceWebEnvironment();
    6. setInitializers((Collection) getSpringFactoriesInstances(
    7. ApplicationContextInitializer.class));
    8. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    9. this.mainApplicationClass = deduceMainApplicationClass();
    10. }
    复制代码
    可以看到做了如下5件事:
    1. 如果sources⻓度⼤于0的话,加⼊到SpringApplication的sources中,该sources是⼀个LinkedHashSet.
    2. 调⽤deduceWebEnvironment⽅法判断是否是web环境
    3. 设置initializers.
    4. 设置Listeners.
    5. 设置mainApplicationClass.

    4. deduceWebEnvironment代码如下:
    1. private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
    2. "org.springframework.web.context.ConfigurableWebApplicationContext" };
    3. private boolean deduceWebEnvironment() {
    4. for (String className : WEB_ENVIRONMENT_CLASSES) {
    5. if (!ClassUtils.isPresent(className, null)) {
    6. return false;
    7. }
    8. }
    9. return true;
    10. }
    复制代码
    可以发现会调⽤ClassUtils类的isPresent⽅法,检查classpath中是否存在javax.servlet.Servlet类和
    org.springframework.web.context.ConfigurableWebApplicationContext类,如果存在的话,返回true.否则返回false.

    5. 在设置Initializers时⾸先调⽤getSpringFactoriesInstances⽅法加载ApplicationContextInitializer.然后直接赋值给initializers.代码如下:
    1. private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
    2. return getSpringFactoriesInstances(type, new Class<?>[] {});
    3. }
    复制代码
    转⽽调⽤如下代码:
    1. private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
    2. Class<?>[] parameterTypes, Object... args) {
    3. ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    4. // Use names and ensure unique to protect against duplicates
    5. // 使⽤Set保存names来避免重复元素
    6. Set<String> names = new LinkedHashSet<String>(
    7. SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    8. // 根据names来进⾏实例化
    9. List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
    10. classLoader, args, names);
    11. // 对实例进⾏排序
    12. AnnotationAwareOrderComparator.sort(instances);
    13. return instances;
    14. }
    复制代码
    该⽅法逻辑如下:
    1. ⾸先获得ClassLoader.
    2. 调⽤SpringFactoriesLoader#loadFactoryNames进⾏加载,然后放⼊到LinkedHashSet进⾏去重.
    3. 调⽤createSpringFactoriesInstances进⾏初始化
    4. 排序
    其中SpringFactoriesLoader#loadFactoryNames代码如下:
    1. public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    2. String factoryClassName = factoryClass.getName();
    3. try {
    4. Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURC
    5. E_LOCATION) :
    6. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    7. List<String> result = new ArrayList<String>();
    8. while (urls.hasMoreElements()) {
    9. URL url = urls.nextElement();
    10. Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
    11. String factoryClassNames = properties.getProperty(factoryClassName);
    12. result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassN
    13. ames)));
    14. }
    15. return result;
    16. }
    17. catch (IOException ex) {
    18. throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
    19. "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    20. }
    21. }
    复制代码
    逻辑如下:
    1. 获得factoryClassName,对于当前来说factoryClassName =org.springframework.context.ApplicationContextInitializer.
    2. 通过传⼊的classLoader加载META-INF/spring.factories⽂件.
    3. 通过调⽤ropertiesLoaderUtils#loadProperties将其转为Properties.
    4. 获得factoryClassName对应的值进⾏返回.
    对于当前来说,由于我们只加⼊了spring-boot-starter-web的依赖,因此会加载如下的配置:
    1. 在spring-boot/META-INF/spring.factories中.org.springframework.context.ApplicationContextInitializer值如下:
    1. org.springframework.context.ApplicationContextInitializer=\
    2. org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
    3. org.springframework.boot.context.ContextIdApplicationContextInitializer,\
    4. org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
    5. org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
    复制代码
    2. 在spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
    中.org.springframework.context.ApplicationContextInitializer值如下:
    1. org.springframework.context.ApplicationContextInitializer=\
    2. org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    3. org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
    复制代码
    因此会加载6个.
    SpringApplication#createSpringFactoriesInstances⽅法如下:
    1. private <T> List<T> createSpringFactoriesInstances(Class<T> type,
    2. Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
    3. Set<String> names) {
    4. List<T> instances = new ArrayList<T>(names.size());
    5. for (String name : names) {
    6. try {
    7. Class<?> instanceClass = ClassUtils.forName(name, classLoader);
    8. Assert.isAssignable(type, instanceClass);
    9. Constructor<?> constructor = instanceClass
    10. .getDeclaredConstructor(parameterTypes);
    11. T instance = (T) BeanUtils.instantiateClass(constructor, args);
    12. instances.add(instance);
    13. }
    14. catch (Throwable ex) {
    15. throw new IllegalArgumentException(
    16. "Cannot instantiate " + type + " : " + name, ex);
    17. }
    18. }
    19. return instances;
    20. }
    复制代码
    逻辑如下:遍历传⼊的names,也就是之前通过SpringFactoriesLoader加载的类名.通过遍历,依次调⽤其构造器进⾏初始化.加⼊到
    instances.然后进⾏返回.
    对于当前场景来说:
    ConfigurationWarningsApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer
    初始化没有做任何事.
    ContextIdApplicationContextInitializer在初始化时.会获得spring boot的应⽤名.搜索路径如下:
    1. spring.application.name
    2. vcap.application.name
    3. spring.config.name
    4. 如果都没有配置的话,返回application.
    代码如下:
    1. private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${s
    2. pring.config.name:application}}}";
    3. public ContextIdApplicationContextInitializer() {
    4. this(NAME_PATTERN);
    5. }
    6. public ContextIdApplicationContextInitializer(String name) {
    7. this.name = name;
    8. }
    复制代码
    6. 设置SpringApplication#setListeners时,还是同样的套路.调⽤getSpringFactoriesInstances加载META-INF/spring.factories中配置
    的org.springframework.context.ApplicationListener. 对于当前来说.加载的类如下:
    1. org.springframework.context.ApplicationListener=\
    2. org.springframework.boot.ClearCachesApplicationListener,\
    3. org.springframework.boot.builder.ParentContextCloserApplicationListener,\
    4. org.springframework.boot.context.FileEncodingApplicationListener,\
    5. org.springframework.boot.context.config.AnsiOutputApplicationListener,\
    6. org.springframework.boot.context.config.ConfigFileApplicationListener,\
    7. org.springframework.boot.context.config.DelegatingApplicationListener,\
    8. org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\
    9. org.springframework.boot.logging.ClasspathLoggingApplicationListener,\
    10. org.springframework.boot.logging.LoggingApplicationListener
    复制代码
    这些类在构造器中都没有做任何事.
    7. 调⽤SpringApplication#deduceMainApplicationClass⽅法.获得应⽤的启动类.该⽅法通过获取当前⽅法调⽤栈,找到main函数的
    类.代码如下:
    1. private Class<?> deduceMainApplicationClass() {
    2. try {
    3. StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
    4. for (StackTraceElement stackTraceElement : stackTrace) {
    5. if ("main".equals(stackTraceElement.getMethodName())) {
    6. return Class.forName(stackTraceElement.getClassName());
    7. }
    8. }
    9. }
    10. catch (ClassNotFoundException ex) {
    11. // Swallow and continue
    12. }
    13. return null;
    14. }
    复制代码
    流程图如下:





您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP