• springboot源码解析


    复制代码
     1 package com.microservice.framework;
     2 
     3 import org.springframework.boot.SpringApplication;
     4 import org.springframework.boot.autoconfigure.SpringBootApplication;
     5 
     6 @SpringBootApplication
     7 public class MySpringAplication {
     8 
     9     public void run(String[] args) {
    10         SpringApplication sa = new SpringApplication(MySpringAplication.class);
    11         sa.run(args);
    12     }
    13 
    14 }
    复制代码

    SpringBoot启动过程:

    1、构建SpringApplication对象

    2、执行run()

    一、构建SpringApplication对象

    1     /**
    2      * The application context will load beans from the specified sources 
    3      */
    4     public SpringApplication(Object... sources) {
    5         initialize(sources);
    6     }

    说明:

    • 实例化该类的时候会加载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列表

    复制代码
     1     private List<ApplicationContextInitializer<?>> initializers;
     2 
     3     public void setInitializers(
     4             Collection<? extends ApplicationContextInitializer<?>> initializers) {
     5         this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
     6         this.initializers.addAll(initializers);
     7     }
     8 
     9     private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
    10         return getSpringFactoriesInstances(type, new Class<?>[] {});
    11     }
    12 
    13     private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
    14             Class<?>[] parameterTypes, Object... args) {
    15         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    16 
    17         // Use names and ensure unique to protect against duplicates
    18         Set<String> names = new LinkedHashSet<String>(
    19                 SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    20         List<T> instances = new ArrayList<T>(names.size());
    21 
    22         // Create instances from the names
    23         for (String name : names) {
    24             try {
    25                 Class<?> instanceClass = ClassUtils.forName(name, classLoader);
    26                 Assert.isAssignable(type, instanceClass);
    27                 Constructor<?> constructor = instanceClass.getConstructor(parameterTypes);
    28                 T instance = (T) constructor.newInstance(args);
    29                 instances.add(instance);
    30             }
    31             catch (Throwable ex) {
    32                 throw new IllegalArgumentException(
    33                         "Cannot instantiate " + type + " : " + name, ex);
    34             }
    35         }
    36 
    37         AnnotationAwareOrderComparator.sort(instances);
    38         return instances;
    39     }
    复制代码

    步骤:

    • 调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)来获取所有Spring Factories的名字,(这里是获取了四个ApplicationContextInitializer实现类的全类名,见下边)
    • 为每一个Spring Factories根据读取到的名字创建其对象。(这里创建了4个对象)
    • 将创建好的对象列表排序并返回。

    其中,SpringFactoriesLoader.loadFactoryNames(type, classLoader)如下:

    复制代码
     1     /**
     2      * The location to look for factories.
     3      * <p>Can be present in multiple JAR files.
     4      */
     5     public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
     6 
     7     /**
     8      * Load the fully qualified class names of factory implementations of the
     9      * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
    10      * class loader.
    11      */
    12     public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    13         String factoryClassName = factoryClass.getName();
    14         try {
    15             Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    16                     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    17             List<String> result = new ArrayList<String>();
    18             while (urls.hasMoreElements()) {
    19                 URL url = urls.nextElement();
    20                 Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
    21                 String factoryClassNames = properties.getProperty(factoryClassName);
    22                 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    23             }
    24             return result;
    25         }
    26         catch (IOException ex) {
    27             throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
    28                     "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    29         }
    30     }
    复制代码

    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列表

    复制代码
     1     private List<ApplicationListener<?>> listeners;    
     2 
     3         /**
     4      * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication
     5      * and registered with the {@link ApplicationContext}.
     6      * @param listeners the listeners to set
     7      */
     8     public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
     9         this.listeners = new ArrayList<ApplicationListener<?>>();
    10         this.listeners.addAll(listeners);
    11     }    
    复制代码

    META-INF/spring-factories

    复制代码
     1 # Application Listeners
     2 org.springframework.context.ApplicationListener=
     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
    复制代码

    以上八个listener的作用如下:

    至此,整个setListeners方法结束,初始化了一个包含以上8个ApplicationListener实例的List集合。

    1.5、初始化主类mainApplicationClass

    复制代码
     1     private Class<?> mainApplicationClass;
     2 
     3     private Class<?> deduceMainApplicationClass() {
     4         try {
     5             StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
     6             for (StackTraceElement stackTraceElement : stackTrace) {
     7                 if ("main".equals(stackTraceElement.getMethodName())) {
     8                     return Class.forName(stackTraceElement.getClassName());
     9                 }
    10             }
    11         }
    12         catch (ClassNotFoundException ex) {
    13             // Swallow and continue
    14         }
    15         return null;
    16     }
    复制代码

    说明:获取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 完成,该文的作者已经解析的很好了,我这里再抄一遍,只是为了加深记忆!!!

    http://www.cnblogs.com/java-zhao/p/5540309.html

  • 相关阅读:
    前端开发在手机UC浏览器上遇到的坑
    前端开发在uc浏览器上遇到的坑
    object-fit 解决图片指定大小被压缩问题
    前端新手需要注意的几个问题
    利用apache搭建本地环境
    有简历,为何还要自我介绍?
    移动端项目总结
    JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember(转载)
    Net Core 控制台程序使用Nlog 输出到log文件
    net core服务器缺包,如何在线安装?
  • 原文地址:https://www.cnblogs.com/softidea/p/6060004.html
Copyright © 2020-2023  润新知