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. 总结

原文地址:https://www.cnblogs.com/whx7762/p/7834485.html