当前位置 博文首页 > springboot源码解析-管中窥豹系列之项目类型(二)

    springboot源码解析-管中窥豹系列之项目类型(二)

    作者:丰极 时间:2021-01-08 18:02

    一、前言

    • Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去。
    • 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot源码管中窥豹系列。

     简介

    二、项目类型

    这一节我们先讨论一下springboot项目的怎么自动加载applicationcontext实现类的。

    • 以前的spring的项目,都是xml加载bean,常用的都是XmlWebApplicationContext实现类
    • 后来出现注解的形式,基本用AnnotationConfigWebApplicationContext实现类
    • 后来又出现响应式编程reactive

    那springboot用的是哪一种呢?

    三、源码解读

    先说结论:关于类型的选择,springboot是根据class来判断的。

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    
        ...
    
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        
        ...
    
    }
    
    • 我们从SpringApplication的构造函数开始看起
    • 构造函数里面有段代码:是确定类型的

    我们先看类型:

    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() {
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }
    
    • 第一段if,如果存在reactive包的DispatcherServlet,同时不存在jersey和mvc的DispatcherHandler,就是REACTIVE
    • 第二段循环,不存在Servlet或者ConfigurableWebApplicationContext,就是none
    • 剩下的就是我们熟悉的SERVLET

    类型确定之后,我们看SpringApplication的run方法:

    public ConfigurableApplicationContext run(String... args) {
            
        ...
    
        try {
            ...
    
            context = createApplicationContext();
            
            ...
        } catch (Throwable ex) {
            
            ...
    
        }
        
        ...
    
        return context;
    }
    

    ApplicationContext实现类就是在createApplicationContext()里面确定的

    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";
    
    public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
            + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
    
    public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
    
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    

    根据类型加载不同的class:

    • 如果是SERVLET:AnnotationConfigServletWebServerApplicationContext
    • 如果是REACTIVE:
      AnnotationConfigReactiveWebServerApplicationContext
    • default:AnnotationConfigApplicationContext

    至此,一目了然了,想要不同的项目类型,添加对应的jar包,springboot自动帮你选择对应的ApplicationContext实现类

    如果是普通的web项目:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    如果是reactive项目:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    

    如果是非web项目:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    

    欢迎关注公众号:丰极,更多技术学习分享。