SpringBoot是如何内置tomcat且整合Spring的?

SpringBoot是如何内置的Tomcat?

 

在SpringBoot之前,可能读者在接触Spring、SpringMVC等框架的web项目都会涉及到一个web.xml,这在笔者SpringMVC源码解析的文章中也是采用的这种方式,为了将我们的DispatcherServlet注册进来,我们会在web.xml里面添加一些东西,如下:


        SpringMVC
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            classpath:spring-mvc.xml
        
        1
        true
    
    
        SpringMVC
        /
    

(注意:建议读者了解一些笔者SpringMVC源码章节的内容再看本章效果更佳)

当然用过SpringBoot的小伙伴们肯定都知道,我们并没有接触到什么web.xml。也没有搞一个Tomcat进去,而是直接就能使用SpringMVC的注解进行项目开发,那么SpringBoot到底做了什么事情呢?下面我们就来对SpringBoot如何内置tomcat以及整合SpringMVC的核心设计进行分享。

没有web.xml,我们能启动SpringMVC吗?

没有web.xml,我们能启动SpringMVC吗?答案是肯定的,首先我们看到Spring官网

SpringBoot是如何内置tomcat且整合Spring的?_第1张图片

上面的意思很简单就是说通常我们的DispatcherServlet作为一个servlet需要在tomcat的web.xml里面声明,但是现在有了更新的方式,通过Java代码进行注册,实现很简单只需要实现WebApplicationInitializer这个接口,下面我们在验证一下这个问题,我们用一个就简单地web项目进行实现

SpringBoot是如何内置tomcat且整合Spring的?_第2张图片

可以看到tomcat在启动的时候进入了这个方法,说明这个方法只要我们实现就能在tomcat启动的时候做一些事情,而这里的参数servletContext我们可以看看他的API,

SpringBoot是如何内置tomcat且整合Spring的?_第3张图片

可以看到它能够添加我们的Servlet,那这就很简单了,我们可以通过实现这个tomcat提供的外置接口来做拓展将我们的SpringMVC封装进去,这就解决了我们的问题“”没有web.xml,我们依旧能启动SpringMVC。

Tomcat如何内置?

在解释如何内置Tomcat的问题中,我们先解释这个问题,在SpringBoot之前我们应该会接触过用Maven插件安装Tomcat,然后启动整个项目,如下

SpringBoot是如何内置tomcat且整合Spring的?_第4张图片

我们点开这个插件可以看到

SpringBoot是如何内置tomcat且整合Spring的?_第5张图片

它内部依赖了很多tomcat的包,这里我们看到一个embed的包,为什么我们提到它,下面就来解释tomcat如何内置的问题。

首先将我们的Spring源码工程加入一些配置

SpringBoot是如何内置tomcat且整合Spring的?_第6张图片

然后我们在写一个test类


  public static void main(String[] args) {
    Tomcat tom = new Tomcat();
    tom.setPort(8080);
      // tomcat启动
      tom.start();
       //阻塞 ,等待前端连接 tom.getServer().await();
      tom.getServer().await();
    }catch (Exception e)
    {
      e.printStackTrace();
    }
   
  }
}

看到这个test类,读者可能有些震惊,,原来tomcat也可以new出来,没错tomcat为什么称为容器这也不是没有道理,就好比我们称Spring容器无非就是Spring内部装了很多Bean对象,而tomcat只是装了很多servlet而已,它依旧能够被我们直接调用,当然目前我们启动的tomcat只是一个空壳容器,下面我们就来模拟SpringBoot如何内置tomcat且整合Spring的。

模拟SpringBoot内置tomcat且整合Spring

首先写一个test

public class Test {

  public static void main(String[] args) {
    Tomcat tom = new Tomcat();
    tom.setPort(8080);
    try{

      File file=new File(System.getProperty("java.io.tmpdir"));
      //获取项目编译后的claess 路径
      String path = Test.class.getResource("/").getPath();

      //获取webapp 文件
      String filePath = new File("src/main/webapp").getAbsolutePath();

      //然后将webapp下的项目添加至tomcat的context容器(context对应一个运行的项目)
      Context context =tom.addWebapp("/",filePath); //参数1:一般是项目名 对应请求url中的项目名
      //webResourceRoot 用于加载 项目的class文件
      WebResourceRoot webResource = new StandardRoot(context);
      webResource.addPreResources(new DirResourceSet(webResource,"/WEB-INF/classes",path,"/"));
      // tomcat启动
      tom.start();
      //阻塞 ,等待前端连接 tom.getServer().await();
      tom.getServer().await();
    }catch (Exception e)
    {
      e.printStackTrace();
    }

  }
}

然后再写一个配置类


@Configuration
@ComponentScan("SpringMVC.**")
/*@EnableWebMvc*/
public class AppConfig {


}

注意ComponentScan的路径,可能和我的不一样,最后在写一个WebApplicationInitializer


public class MyWebApplicationInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletCxt) {

    // Load Spring web application configuration
    AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
    ac.register(AppConfig.class);
    ac.refresh();

    // Create and register the DispatcherServlet
    DispatcherServlet servlet = new DispatcherServlet(ac);
    ServletRegistration.Dynamic registration = servletCxt.addServlet("SpringMVC", servlet);
    registration.setLoadOnStartup(1);
    registration.addMapping("/*");
    System.out.println("WebApplicationInitializer");
  }
}

好了准备工作完毕我们写一个测试类


@Controller
public class UserController {

  @RequestMapping("/home")
  public String home(){
    System.out.println("-------------");
    return "home";
  }

}

运行我们的tomcat,运行完毕后我们看到了我们的回调函数已被执行

SpringBoot是如何内置tomcat且整合Spring的?_第7张图片

并且已经初始化了我们的SpringMVC

SpringBoot是如何内置tomcat且整合Spring的?_第8张图片

我们在测试一下我们的UserController

SpringBoot是如何内置tomcat且整合Spring的?_第9张图片

可以看到我们内置成功,而SpringBoot是这样做的吗?这里笔者可以说明SpringBoot不是这样做的,我们先看到Test类的   Context context =tom.addWebapp("/",filePath); 这段代码,我们可以看到我们执行了addwebapp,这句代码是什么意思?就是说我们将这个项目当成为了web项目,那么什么是web项目?个人认为web项目和普通项目的区别就是有一个包路径叫webapp,因此如果我们执行的addwebapp,那么tomcat就会认为我们的项目是web项目,于是它就会去做很多很多的事情包括我们的WebApplicationInitializer ,我们测试一下如果我们去掉addwebapp

public class Test {

  public static void main(String[] args) {
    Tomcat tom = new Tomcat();
    tom.setPort(8080);
    try{

      File file=new File(System.getProperty("java.io.tmpdir"));
      //获取项目编译后的claess 路径
      String path = Test.class.getResource("/").getPath();

      //获取webapp 文件
      String filePath = new File("src/main/webapp").getAbsolutePath();

      //然后将webapp下的项目添加至tomcat的context容器(context对应一个运行的项目)
      //Context context =tom.addWebapp("/",filePath); //参数1:一般是项目名 对应请求url中的项目名
      Context context =tom.addContext("/",filePath);

      //webResourceRoot 用于加载 项目的class文件
      WebResourceRoot webResource = new StandardRoot(context);
      webResource.addPreResources(new DirResourceSet(webResource,"/WEB-INF/classes",path,"/"));
      // tomcat启动
      tom.start();
      //阻塞 ,等待前端连接 tom.getServer().await();
      tom.getServer().await();
    }catch (Exception e)
    {
      e.printStackTrace();
    }

  }
}

让我们将项目改成  Context context =tom.addContext("/",filePath);

SpringBoot是如何内置tomcat且整合Spring的?_第10张图片

我们的日志就不会打印WebApplicationInitializer,并且也不会显示找不到web.xml这条日志,也就是说SpringBoot的目的就是想要完全脱离传统的web工程的模式,这也是为什么SpringBoot适合用来做前后端分离的原因。好了回到我们的主题,那么既然不能回调我们的WebApplicationInitializer方法,那么SpringBoot又如何去启动我们的Spring呢?这也很简单,我们直接将Spring放入我们的Main函数里如下:


public class Test {

  public static void main(String[] args) {
    Tomcat tom = new Tomcat();
    tom.setPort(8080);
    try{

      File file=new File(System.getProperty("java.io.tmpdir"));
      //获取项目编译后的claess 路径
      String path = Test.class.getResource("/").getPath();

      //获取webapp 文件
      String filePath = new File("src/main/webapp").getAbsolutePath();

      //然后将webapp下的项目添加至tomcat的context容器(context对应一个运行的项目)
      //Context context =tom.addWebapp("/",filePath); //参数1:一般是项目名 对应请求url中的项目名
      Context context =tom.addContext("/",filePath);

      //webResourceRoot 用于加载 项目的class文件
      WebResourceRoot webResource = new StandardRoot(context);
      webResource.addPreResources(new DirResourceSet(webResource,"/WEB-INF/classes",path,"/"));

      AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
      ac.register(AppConfig.class);
      ac.refresh();

      // Create and register the DispatcherServlet
      DispatcherServlet servlet = new DispatcherServlet(ac);
      tom.addServlet(context,"SpringMVC",servlet);
      context.addServletMapping("/*","SpringMVC");
      // tomcat启动
      tom.start();
      //阻塞 ,等待前端连接 tom.getServer().await();
      tom.getServer().await();
    }catch (Exception e)
    {
      e.printStackTrace();
    }

  }
}

然后测试同样会有这样的效果

SpringBoot是如何内置tomcat且整合Spring的?_第11张图片

 

你可能感兴趣的:(Spring源码,SpringMVC,SpringBoot,java,spring)