zoukankan      html  css  js  c++  java
  • Spring boot启动原理

    1.入口类

    /**
     * springboot应用的启动入口
    */
    @RestController
    @SpringBootApplication
    public class SampleApplication {
        
        @RequestMapping("/")
        public String sayhello(){
            return "hello world";
        }
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            SpringApplication.run(SampleApplication.class, args);
        }
    
    }

    Spring Boot程序的启动入口就一行代码,SpringApplication.run(SampleApplication.class,args)

    2.执行过程

    2.1SpringApplication的实例化

    SpringApplication的静态run方法内部其实是new了一个SpringApplication的实例,构造函数保存了SourceClass。在SpringApplication实例初始化的时候,它做了几件事情:

    •  推断应用类型是standard还是Web。根据classpath里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为Web应用使用的ApplicationContext类型。
    • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationContextInitializer。SpringFactoriesLoader从类路径下各个jar的配置文件META-INF/spring.factories加载配置
    • 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
    • 推断应用入口类

        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 = deduceWebApplicationType();
            setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            this.mainApplicationClass = deduceMainApplicationClass();
        }

    下面具体从源码来看看每一步是怎么做的

    2.1  推断应用类型是Standard还是Web

    private WebApplicationType deduceWebApplicationType() {
            if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
                    && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
                return WebApplicationType.REACTIVE;
            }
            for (String className : WEB_ENVIRONMENT_CLASSES) {
                if (!ClassUtils.isPresent(className, null)) {
                    return WebApplicationType.NONE;
                }
            }
            return WebApplicationType.SERVLET;
        }

    //相关常量
     private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
              + "web.reactive.DispatcherHandler";
    
    

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

      private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
             "org.springframework.web.context.ConfigurableWebApplicationContext" };

    可能会出现三种结果:

    1. WebApplicationType.REACTIVE - 当类路径中存在REACTIVE_WEB_ENVIRONMENT_CLASS并且不存在MVC_WEB_ENVIRONMENT_CLASS时
    2. WebApplicationType.NONE - 也就是非Web型应用(Standard型),此时类路径中不包含WEB_ENVIRONMENT_CLASSES中定义的任何一个类时
    3. WebApplicationType.SERVLET - 类路径中包含了WEB_ENVIRONMENT_CLASSES中定义的所有类型时

    2.2 设置初始化器ApplicationContextInitializer

    setInitializers((Collection) getSpringFactoriesInstances(
                    ApplicationContextInitializer.class));

    这里出现了一个新的概念 - 初始化器。

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
            return getSpringFactoriesInstances(type, new Class<?>[] {});
        }
    
    // 这里的入参type就是ApplicationContextInitializer.class
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
                Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            // Use names and ensure unique to protect against duplicates
            Set<String> names = new LinkedHashSet<>(
                    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                    classLoader, args, names);
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
        }

    这里面首先会根据入参type读取所有的names(是一个String集合),然后根据这个集合来完成对应的实例化操作。

    // 这里的入参type就是ApplicationContextInitializer.class
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
            String factoryClassName = factoryClass.getName();
            return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
        }
    
        private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
            MultiValueMap<String, String> result = cache.get(classLoader);
            if (result != null)
                return result;
            try {
                Enumeration<URL> urls = (classLoader != null ?
                        classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                        ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
                result = new LinkedMultiValueMap<>();
                while (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    for (Map.Entry<?, ?> entry : properties.entrySet()) {
                        List<String> factoryClassNames = Arrays.asList(
                                StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
                        result.addAll((String) entry.getKey(), factoryClassNames);
                    }
                }
                cache.put(classLoader, result);
                return result;
            }
            catch (IOException ex) {
                throw new IllegalArgumentException("Unable to load factories from location [" +
                        FACTORIES_RESOURCE_LOCATION + "]", ex);
            }
        }

    /**
    * The location to look for factories.
    * <p>Can be present in multiple JAR files.
    */
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

     

    这个方法会尝试从类路径的META-INF/spring.factories处读取相应配置文件,然后进行遍历,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure这个包为例,它的META-INF/spring.factories部分定义如下所示:

    # Initializers
    org.springframework.context.ApplicationContextInitializer=
    org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
    org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

    因此这两个类名会被读取出来,然后放入到集合中,准备开始下面的实例化操作:

    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;
        }

    类加载,确认被加载的类确实是org.springframework.context.ApplicationContextInitializer的子类,然后就是得到构造器进行初始化最后放入到实例列表中。

    因此,所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,这个接口是这样定义的:

    public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    
        /**
         * Initialize the given application context.
         * @param applicationContext the application to configure
         */
        void initialize(C applicationContext);
    
    }

    根据类文档,这个接口的主要功能是:

    在Spring上下文被刷新之前进行初始化的操作。典型地比如在Web应用中,注册Property Sources或者是激活Profiles。Property Sources比较好理解,就是配置文件。Profiles是Spring为了在不同环境下(如DEV,TEST,PRODUCTION等),加载不同的配置项而抽象出来的一个实体。

    2.3 设置监听器ApplicationListener

    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 这里的入参type是:org.springframework.context.ApplicationListener.class
    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) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }

     

    可以发现,这个加载相应的类名,然后完成实例化的过程和上面在设置初始化器时如出一辙,同样,还是以spring-boot-autoconfigure这个包中的spring.factories为例,看看相应的Key-Value:

    # Application Listeners
    org.springframework.context.ApplicationListener=
    org.springframework.boot.autoconfigure.BackgroundPreinitializer

    至于ApplicationListener接口,它是Spring框架中一个相当基础的接口了,代码如下:

       

    @FunctionalInterface
    public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    
        /**
         * Handle an application event.
         * @param event the event to respond to
         */
        void onApplicationEvent(E event);
    
    }

    2.4  推断应用入口类

    this.mainApplicationClass = deduceMainApplicationClass();

    这个方法的实现有点意思:

    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实例的初始化过程就结束了。

    2.2  执行SpringApplication实例的run方法

    SpringApplication实例的run方法内部都做了哪些呢:

    1.创建SpringApplication自己的SpringApplicationRunListener。遍历执行所有通过SpringFactoriesLoader可以查找到并加载的SpringApplicationRunListener。调用.它们的started()方法,告诉这些SpringApplicationRunListener,“嘿,SpringBoot应用要开始执行咯!”。

    2.创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)。

    3. 遍历调用所有SpringApplicationRunListener的environmentPrepared()的方法,告诉他们:“当前SpringBoot应用使用的Environment准备好了咯!”。

    4. 如果SpringApplication的showBanner属性被设置为true,则打印banner。

    5.创建ApplicationContext,因为现在应用类型是Servlet,创建的是AnnotationConfigServletWebServerApplicationContext。

    6.对ApplicationContext进行配置,将之前准备好的Environment设置给创建好的ApplicationContext使用。还有配置BeanNameGenerator、ResourceLoader等。

    7.ApplicationContext创建好之后,然后遍历ApplicationContextInitializer的initialize(applicationContext)方法来对已经创建好的ApplicationContext进行进一步的处理。这里的ApplicationContextInitializer就是创建SpringApplication实例时设置的初始化器。

    8. 遍历调用所有SpringApplicationRunListener的contextPrepared()方法。

    9. 最核心的一步,将之前通过@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备完毕的ApplicationContext。

    10.遍历调用所有SpringApplicationRunListener的contextLoaded()方法。

    11.启动ApplicationContext,AnnotationConfigServletWebServerApplicationContext继承了SpringFrameWork本身提供的GenericWebApplicationContext提供的功能并进行了扩展,以支持配置并启动Embed Tomcat。

                 (1)、对BeanFactory进行一些初始化配置。

         (2)、执行BeanFactoryPostProcessor,其中包括对BeanDefinition的进一步处理。最重要的是ConfigurationClassPostProcessor,用来解析处理所有@Configuration标签类,并将Bean定义注册到BeanFactory中。因为@SpringBootApplication中包含了@EnableAutoConfiguration的meta-annotation,会进行自动配置处理,基本原理是判断工程依赖了哪些第三方组件并对其进行自动化配置,这样处理完@Configuration标签后,BeanFactory中就已经有大量的Bean定义了。

        (3)、注册BeanPostProcessor,这些Processor会在首次getBean时执行。主要功能包括进行Autowire、Required等标签的处理,完成自动绑定等功能。也有特殊的关于WebServleterFactory的后续处理。

        (4)、在ApplicationContext的onRefresh方法中会对Web容器(Tomcat)进行配置,包括注册Servlet、Filter、Listener等。

        (5)、在ApplicationContext的finishRefresh方法中启动Web容器(Tomcat),完成应用的启动。

    3. 总结

  • 相关阅读:
    Python的数据类型--数字--字符串
    python基本--数据类型
    系统分区 ,硬盘格式化,
    linux 用户创建,权限,分组
    协程
    进程
    线程与进程--线程三把锁
    线程
    socket网络编程-字典
    socket网络编程
  • 原文地址:https://www.cnblogs.com/whx7762/p/7834485.html
Copyright © 2011-2022 走看看