spring boot 源码解析-SpringApplication初始化_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > spring boot 源码解析-SpringApplication初始化

spring boot 源码解析-SpringApplication初始化

 2018/5/15 18:11:54  小开发仔  程序员俱乐部  我要评论(0)
  • 摘要:前?我们?成?个springboot项?时,会?带?个启动类.代码如下:@SpringBootApplicationpublicclassSpringBootAnalysisApplication{publicstaticvoidmain(String[]args){SpringApplication.run(SpringBootAnalysisApplication.class,args);}}就是这么简单的代码,构成了springboot的世界
  • 标签:源码 Spring 解析 APP

前?

我们?成?个spring boot?项?时,会?带?个启动类. 代码如下:

class="java">@SpringBootApplication
public class SpringBootAnalysisApplication {
 public static void main(String[] args) {
 SpringApplication.run(SpringBootAnalysisApplication.class, args);
 }
}

?就是这么简单的代码,构成了spring boot的世界. 那么代码中只有?个@SpringBootApplication 注解 和 调?了SpringApplication#run

?法.那么我们先来解析SpringApplication的run?法.

?

解析

  1. ?先调?了org.springframework.boot.SpringApplication#run(Object, String...) ?法.代码如下:

  2. public static ConfigurableApplicationContext run(Object source, String... args) {
     return run(new Object[] { source }, args);
    }
    ?
  3. 接着调?如下代码:
  4. public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
     return new SpringApplication(sources).run(args);
    }
    ?
  5. 可以发现 ?先初始化了SpringApplication,然后调?其实例?法:run.

    ?

    2. 在 SpringApplication 的构造器中,调?了 initialize ?法.

  6. public SpringApplication(Object... sources) {
     initialize(sources);
    }
    ?
  7. 3. SpringApplication#initialize?法代码如下:
  8. 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();
    }
    ?
  9. 可以看到做了如下5件事:

    1. 如果sources?度?于0的话,加?到SpringApplication的sources中,该sources是?个LinkedHashSet.

    2. 调?deduceWebEnvironment?法判断是否是web环境

    3. 设置initializers.

    4. 设置Listeners.

    5. 设置mainApplicationClass.

    ?

    4. deduceWebEnvironment代码如下:

  10. 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;
    }
    ?
  11. 可以发现会调?ClassUtils类的isPresent?法,检查classpath中是否存在javax.servlet.Servlet类和

    org.springframework.web.context.ConfigurableWebApplicationContext类,如果存在的话,返回true.否则返回false.

    ?

    5. 在设置Initializers时?先调?getSpringFactoriesInstances?法加载ApplicationContextInitializer.然后直接赋值给initializers.代码如下:

  12. private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
     return getSpringFactoriesInstances(type, new Class<?>[] {});
    }
    ?
  13. 转?调?如下代码:
  14. 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;
    }
    ?
  15. 该?法逻辑如下:

    1. ?先获得ClassLoader.

    2. 调?SpringFactoriesLoader#loadFactoryNames进?加载,然后放?到LinkedHashSet进?去重.

    3. 调?createSpringFactoriesInstances进?初始化

    4. 排序

    其中SpringFactoriesLoader#loadFactoryNames代码如下:

  16. 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);
     }
    }
    ?
  17. 逻辑如下:

    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值如下:

  18. 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
    ?
  19. 2. 在spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

    中.org.springframework.context.ApplicationContextInitializer值如下:

  20. org.springframework.context.ApplicationContextInitializer=\
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
    org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
    ?
  21. 因此会加载6个.

    SpringApplication#createSpringFactoriesInstances?法如下:

  22. 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;
    }
    ?
  23. 逻辑如下:遍历传?的names,也就是之前通过SpringFactoriesLoader加载的类名.通过遍历,依次调?其构造器进?初始化.加?到

    instances.然后进?返回.

    对于当前场景来说:

    ConfigurationWarningsApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer

    初始化没有做任何事.

    ContextIdApplicationContextInitializer在初始化时.会获得spring boot的应?名.搜索路径如下:

    1. spring.application.name

    2. vcap.application.name

    3. spring.config.name

    4. 如果都没有配置的话,返回application.

    代码如下:

  24. 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;
    }
    ?
  25. 6. 设置SpringApplication#setListeners时,还是同样的套路.调?getSpringFactoriesInstances加载META-INF/spring.factories中配置

    的org.springframework.context.ApplicationListener. 对于当前来说.加载的类如下:

  26. 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
    ?
  27. 这些类在构造器中都没有做任何事.

    7. 调?SpringApplication#deduceMainApplicationClass?法.获得应?的启动类.该?法通过获取当前?法调?栈,找到main函数的

    类.代码如下:

  28. 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;
    }
    ?
  29. 流程图如下:

  30. ?
  31. DBAEDA575093D9D0.png" alt="">参考视频教程:Spring Boot源码解析

    ?

    小程序

    ?

    images/daAPh22Xmjaz4RaPaRfBrH4dWRSKjeaS.jpg

  • 大小: 134.7 KB
  • 查看图片附件
上一篇: 原子操作 下一篇: 没有下一篇了!
发表评论
用户名: 匿名