美文网首页
SpringBoot2.0深度实践学习笔记(五)之 Spring

SpringBoot2.0深度实践学习笔记(五)之 Spring

作者: Sam_L | 来源:发表于2019-04-29 12:27 被阅读0次
SpringApplication准备阶段
第二部分 推断Web应用类型和主引导类(Main Class)

推断Web应用类型
规则:
根据当前应用 ClassPath 中是否存在相关实现类来推断 Web 应用的类型,包括:

  • Web Reactive: WebApplicationType.REACTIVE
  • Web Servlet: WebApplicationType.SERVLET
  • 非 Web: WebApplicationType.NONE

(本人使用的springboot版本是2.1.4.RELEASE)
在SpringApplication.java 中有getWebApplicationType() setWebApplicationType()

/**
     * Returns the type of web application that is being run.//返回正在运行的web应用程序的类型。
     * @return the type of web application
     * @since 2.0.0
     */
    public WebApplicationType getWebApplicationType() {
        return this.webApplicationType;
    }

    /**
     * Sets the type of web application to be run. If not explicitly set the type of web //设置要运行的web应用程序的类型。如果没有显式设置web的类型
     * application will be deduced based on the classpath.//将根据类路径推导应用程序。
     * @param webApplicationType the web application type
     * @since 2.0.0
     */
    public void setWebApplicationType(WebApplicationType webApplicationType) {
        Assert.notNull(webApplicationType, "WebApplicationType must not be null");
        this.webApplicationType = webApplicationType;
    }

WebApplicationType 源码:

/**
 * An enumeration of possible types of web application.
 *
 * @author Andy Wilkinson
 * @author Brian Clozel
 * @since 2.0.0
 */
public enum WebApplicationType {

    /**
     * The application should not run as a web application and should not start an
     * embedded web server.
     */
    NONE,

    /**
     * The application should run as a servlet-based web application and should start an
     * embedded servlet web server.
     */
    SERVLET,

    /**
     * The application should run as a reactive web application and should start an
     * embedded reactive web server.
     */
    REACTIVE;

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

    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

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

    static WebApplicationType deduceFromApplicationContext(
            Class<?> applicationContextClass) {
        if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
            return WebApplicationType.SERVLET;
        }
        if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
            return WebApplicationType.REACTIVE;
        }
        return WebApplicationType.NONE;
    }

    private static boolean isAssignable(String target, Class<?> type) {
        try {
            return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
        }
        catch (Throwable ex) {
            return false;
        }
    }

}

说明:
1)springboot-2.0的版本的SpringApplication.java里有个deduceWebApplicationType类,

1.png

但是在springboot2.1.4.RELEASE看不见这个类了,应该替换为WebApplicationType的这个deduceFromClasspath()


2.png

deduceFromClasspath() 这里有个判断条件:
当这个类DispatcherHandler类存在的时候(WEBFLUX_INDICATOR_CLASS = "org."+"springframework.web.reactive.DispatcherHandler";),
是webflux/reactive web里面的一个必然的类型,
并且另外的类DispatcherServlet不存在的时候( WEBMVC_INDICATOR_CLASS = "org.springframework."+"web.servlet.DispatcherServlet";)
会返回REACTIVE

这两个环境是不能共存的。
之前你会发现webmvc和webflux同时存在的时候,webflux没有被激活,原因就在这个地方。

2)foreach循环
当Servlet和ConfigurableWebApplicationContext都不存在的时候,
( SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };)
会返回非web类型

除此之外就会返回SERVLET
SERVLET存在的时候就会覆盖掉那2个

进行这样的推断是希望你少去配置。

3)你可以强制对类型进行关闭
例如:


/**
 * {@link SpringApplication} 引导类
 */

public class SpringApplicationBootstrap {
    public static void main(String[] args) {

        //SpringApplication.run(ApplicationConfiguration.class,args);

        Set<String> sources = new HashSet();
        //配置class名称
        sources.add(ApplicationConfiguration.class.getName());
        SpringApplication springApplication = new SpringApplication();
        springApplication.setSources(sources);
 
//配置这个。现在推断的类型是 web servlet类型,强制关闭掉就变成一个普通类型。springApplication.setWebApplicationType(WebApplicationType.NONE);
        ConfigurableApplicationContext context =springApplication.run(args);
        //System.out.println("Bean:" + context.getBean(ApplicationConfiguration.class));
    }

    @SpringBootApplication
    public static class ApplicationConfiguration {

    }

}

运行之后,发现主线程已经被kill掉了,因为,如果是web容器的话会有阻塞的过程,阻塞当前线程不被终止,你会接受不同的请求来响应这个处理,所以这里不是web的话就会变成一个普通的方式。

推断引导类(Main Class)

通常引导类Main Class是放在SpringApplication.run方法参数里面的

SpringApplication.run(DiveInSpringBootApplication.class, args);

我们下面的栗子就没有将Main Class放进,是通过ApplicationConfiguration放进去的

 sources.add(ApplicationConfiguration.class.getName());

这个时候就不知道Main Class是哪一个,Main Class是如何进行推导的,

先来看源码吧
**SpringApplication.java中的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;
    }
/**
 * An element in a stack trace, as returned by {@link
 * Throwable#getStackTrace()}.  Each element represents a single stack frame.
 * All stack frames except for the one at the top of the stack represent
 * a method invocation.  The frame at the top of the stack represents the
 * execution point at which the stack trace was generated.  Typically,
 * this is the point at which the throwable corresponding to the stack trace
 * was created.
 *
 * @since  1.4
 * @author Josh Bloch
 */
public final class StackTraceElement implements java.io.Serializable {
    // Normally initialized by VM (public constructor added in 1.5)
    private String declaringClass;//当前的类
    private String methodName;//当前的方法
    private String fileName;
    private int    lineNumber;

说明:
这个实现是通过异常堆栈实现的,通过堆栈去判断

结合【栗子】再来具体看一下~

public class SpringApplicationBootstrap {
    public static void main(String[] args) {

        //SpringApplication.run(ApplicationConfiguration.class,args);

        Set<String> sources = new HashSet();
        //配置class名称
        sources.add(ApplicationConfiguration.class.getName());
        SpringApplication springApplication = new SpringApplication();
        springApplication.setSources(sources);
        springApplication.setWebApplicationType(WebApplicationType.NONE);
        ConfigurableApplicationContext context =springApplication.run(args);
        //System.out.println("Bean:" + context.getBean(ApplicationConfiguration.class));
    }

    @SpringBootApplication
    public static class ApplicationConfiguration {

    }

}

当执行这段代码的时候是启动一个线程,我们通过debug方式启动,跟踪一下,当跳到这里的时候,会有一个main的线程

4.png
3.png

我们可以看见当前线程的堆栈信息

5.png
堆栈信息说明:
1)0:当前执行方法的Thread.currentThread().getStackTrace()
2:)1:main方法---spring判断的就是main方法是不是这个

这里就已经推断出了Main Class是当前的类SpringApplicationBootstrap

我们再继续调试
会看到4个栈,其中一个栈就是SpringApplicationBootstrap

6.png
遍历的时候就会遍历到main方法里

这就是通过推断方式进行推导Main Class的。

相关文章

网友评论

      本文标题:SpringBoot2.0深度实践学习笔记(五)之 Spring

      本文链接:https://www.haomeiwen.com/subject/lbeenqtx.html