【java】(一)SpringBoot 源码解析——SpringApplication 初始化()

  本篇文章为你整理了【java】(一)SpringBoot 源码解析——SpringApplication 初始化()的详细内容,包含有 【java】(一)SpringBoot 源码解析——SpringApplication 初始化,希望能帮助你了解 【java】(一)SpringBoot 源码解析——SpringApplication 初始化。

  深入学习springboot笔记系列,可能会有错误还请指正,互相勉励,互相学习。

  SpringBoot 项目启动只需启动 主类的 main 函数即可启动java服务,相比于以往的部署java服务简化方便了很多,接下我们从主函数入手一步一步剖析源码是如何通过main函数启动服务的。

  2.SpringBoot 项目程序入口

  主函数通过一个静态 run 方法完成整个服务的构建。

  

@SpringBootApplication

 

  public class LogicalApplication

  {
 

   public static void main(String[] args)

   SpringApplication.run(LogicalApplication.class, args);

  }

 

  接下来看看静态的 run 方法的内部实现。

  2.1.run 方法构造

  

public static ConfigurableApplicationContext run(Class ? primarySource, String... args) {
 return run(new Class ? [] { primarySource }, args);
}

 

  

public static ConfigurableApplicationContext run(Class ? [] primarySources, String[] args) {
 return new SpringApplication(primarySources).run(args);
}

 

  以上通过 new SpringApplication(primarySources) 执行了初始化的一些相关操作。

  3.SpringApplication 初始化

  public SpringApplication(Class ? ... primarySources) {
this(null, primarySources);
}

  public SpringApplication(ResourceLoader resourceLoader, Class ? ... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet (Arrays.asList(primarySources));
// 推断服务类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 初始化 META-INF/spring.factories 中 所有的 BootstrapRegistryInitializer 类
this.bootstrapRegistryInitializers = new ArrayList (getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 初始化 META-INF/spring.factories 中 所有的 ApplicationContextInitializer 类
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 初始化 META-INF/spring.factories 中 所有的 ApplicationListener 类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断主类
this.mainApplicationClass = deduceMainApplicationClass();
}

  
SpringApplication 初始化主要初始化了resourceLoader、primarySources、webApplicationType 、bootstrapRegistryInitializers、initializers、listeners、mainApplicationClass 这几个对象。

  其中resourceLoader 默认为null,primarySources 则为主类的有序去重集合。

  3.1.webApplicationType 推断当前项目启动类型

  

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",

 

   "org.springframework.web.context.ConfigurableWebApplicationContext" };

  private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

  private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

  private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

  
static WebApplicationType deduceFromClasspath() {
//webflux 响应式

   if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)

   !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {

   return WebApplicationType.REACTIVE;

   }
// 是否web 项目

   for (String className : SERVLET_INDICATOR_CLASSES) {

   if (!ClassUtils.isPresent(className, null)) {

   return WebApplicationType.NONE;

   return WebApplicationType.SERVLET;

  }

 

  此处使用ClassUtils.isPresent(String className, @Nullable ClassLoader classLoader) 方法为内部调用类加载器加载相应的类,如果未找到则抛出ClassNotFoundException异常。类加载WEBMVC_INDICATOR_CLASS、WEBFLUX_INDICATOR_CLASS 、JERSEY_INDICATOR_CLASS 几种不同的 Servlet,如果未加载到则为false,从而推断项目启动类型。此处我们是web项目,因此最后的返回值是WebApplicationType.SERVLET。

  3.2.bootstrapRegistryInitializers 初始化

  

 

 

   private T Collection T getSpringFactoriesInstances(Class T type) {

  

return getSpringFactoriesInstances(type, new Class ? [] {});

 

  private T Collection T getSpringFactoriesInstances(Class T type, Class ? [] parameterTypes, Object... args) {
//获取默认类加载器AppClassLoader

   ClassLoader classLoader = getClassLoader();

   //使用默认类加载器加载META-INFO/spring.factories 中配置的类

   Set String names = new LinkedHashSet (SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//使用反射将上一步 类加载完成的 names中的类实例化

   List T instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 将实例化的类排序 排序规则:继承了 PriorityOrdered 接口的 优先级最高, 其次实现Ordered 接口 或者添加@Order 注解 通过比较order值的大小来排序,值越小优先级越高。

   AnnotationAwareOrderComparator.sort(instances);

   return instances;

  @SuppressWarnings("unchecked")

  private T List T createSpringFactoriesInstances(Class T type, Class ? [] parameterTypes,

   ClassLoader classLoader, Object[] args, Set String names) {

   List T instances = new ArrayList (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;

  public static List String loadFactoryNames(Class ? factoryType, @Nullable ClassLoader classLoader) {

   ClassLoader classLoaderToUse = classLoader;

   if (classLoaderToUse == null) {

   classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();

   String factoryTypeName = factoryType.getName();

   return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());

  private static Map String, List String loadSpringFactories(ClassLoader classLoader) {
// 缓存cache 中查找

   Map String, List String result = cache.get(classLoader);

   if (result != null) {

   return result;

   result = new HashMap ();

   try {
// 此处使用类加载器读取所有依赖的包中 MEAT-INFO/spring.factories 中配置的类,并添加在缓存cache中

   Enumeration URL urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);

   while (urls.hasMoreElements()) {

   URL url = urls.nextElement();

   UrlResource resource = new UrlResource(url);

   Properties properties = PropertiesLoaderUtils.loadProperties(resource);

   for (Map.Entry ?, ? entry : properties.entrySet()) {

   String factoryTypeName = ((String) entry.getKey()).trim();

   String[] factoryImplementationNames =

   StringUtils.commaDelimitedListToStringArray((String) entry.getValue());

   for (String factoryImplementationName : factoryImplementationNames) {

   result.computeIfAbsent(factoryTypeName, key - new ArrayList ())

   .add(factoryImplementationName.trim());

   // Replace all lists with unmodifiable lists containing unique elements

   result.replaceAll((factoryType, implementations) - implementations.stream().distinct()

   .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));

   cache.put(classLoader, result);

   catch (IOException ex) {

   throw new IllegalArgumentException("Unable to load factories from location [" +

   FACTORIES_RESOURCE_LOCATION + "]", ex);

   return result;

  }

 

  以上方法通过默认的类加载AppClassLoader 加载依赖包中 META-INFO/spring.factories 路径中所有配置的类并缓存在cache中,然后由class 类型找出缓存cache 中需要相应加载的类并通过反射将类实例化并排序返回。

  3.3.initializers 、listeners初始化

  同 bootstrapRegistryInitializers 加载流程一致,通过cache 缓存提高加载速度。
 

  3.4.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方法的堆栈信息中获取主类的信息。因为SpringApplication的构造器primarySources 是数组类型,因此无法直接通过primarySource 来判断main方法属于哪个class。

  至此 SpringApplication类初始化加载完成。

  1.通过类加载器加载依赖的servlet判断项目类型;

  2.通过类加载器加载指定路径文件 MEAT-INFO/spring.factories 读取指定配置文件并使用cache 缓存提高加载速度;

  3.通过java反射的方式将指定的BootstrapRegistryInitializer.class、ApplicationContextInitializer.class、ApplicationListener.class 类的实现类实例化;

  4.通过运行异常的堆栈信息推断main方法所在的类为主类。

  

  下一章 将继续 讲解 SpringBoot 后续启动流程,谢谢观看。

  以上就是【java】(一)SpringBoot 源码解析——SpringApplication 初始化()的详细内容,想要了解更多 【java】(一)SpringBoot 源码解析——SpringApplication 初始化的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: