嵌入式tomcat分析,并手写加载springMvc

1. 简介

spring boot的嵌入式容器用起来很爽,所以博主也想搞一个类似可以不依赖外部容器的轮子.

虽然可以直接用maven的插件 tomcatX-maven-plugin  完成,但是高版本的更新太慢,所以有了以下的轮子.

2. tomcat结构介绍

为什么要先放这张图嗯,因为嵌入式的tomcat API 设计也是按照这个结构设计的.博主并不想看他的API文档,想靠这张图猜一下他的API如何使用.

3 maven坐标引入

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-core</artifactId>
            <version>8.5.34</version>
        </dependency>

这里选择了热门的tomcat8.5

4 . 入门demo

首先作为入门级的demo当然是要简单,目录结构如下

其中MyWeb 这个类是一个非常简单的servlet 测试类,代码如下

public class MyWeb extends HttpServlet {

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        res.setCharacterEncoding("utf-8");
        System.out.println("进入请求");
        PrintWriter writer = res.getWriter();
        writer.println("<h1>HttpServlet request</h1>");
    }
}

问题1:为什么我要在target目录下创建WEB-INF

嵌入式的tomcat有2种加载servlet的模式

      1. 直接从代码里创建对象,加入容器

       2.和外部的tomcat一样,从WEB-INF目录下的web.xml文件中加载,这里我把 target目录当做应用根目录,自然要手动创建.当然也可以手动指定web.xml的路径,这里后面会提及.

=================框架准备好.撸起袖子就是干我是分割线======================================

通过上面的结构图发现,:

   tomcat下有一个server,一个server下有多个service,然后一个service下有多个connector(连接器,用于接收请求),一个Container(正真处理请求的)

同样的结构在tomcat的 conf/server.xml 配置文件里也可以体现

其中配置文件里的Engine就是Container

根据这个关系,可以构建一个最简单的项目结构

        Tomcat tomcat = new Tomcat();
        //Server server = tomcat.getServer();

        //看源码发现,他只能设置一个service,直接拿默认的
        Service service = tomcat.getService();

        //创建连接器,并且添加对应的连接器,同时连接器指定端口
        Connector connector = new Connector();
        connector.setPort(8888);
        service.addConnector(connector);

        //创建一个引擎,放入service中
        Engine engine = new StandardEngine();
        service.setContainer(engine);
        engine.setDefaultHost("localhost");
        engine.setName("myTomcat");

        //添加host
        Host host = new StandardHost();
        engine.addChild(host);
        host.setName("localhost");
        host.setAppBase("webapps");

别看API挺多,其实对照着 server.xml 文件可以很轻易写出来,

然后就是把我们的项目挂载上去了.

用过tomcat都知道,tomcat的挂载方式,这里我们也对应把xml文件翻译成对应的java代码

<Host>
    <Context path="/前缀" docBase="项目路径" reloadable="true开启热更新">
</Host>
//在对应的host下面创建一个 context 并制定他的工作路径,会加载该目录下的所有class文件,或者静态文件
       StandardContext context = (StandardContext) tomcat.addContext(host, "/", "/Users/bignosecat/IdeaProjects/xx/tomcat/target");

       //初始化ContextConfig配置
        context.addLifecycleListener(new ContextConfig());

这里我直接把我整个项目的编译路径当做项目路径挂载去了tomcat

最后把自己写的servlet加入进去.

 //创建一个servlet
  Wrapper myservlet = new StandardWrapper();
  myservlet.setServlet(new MyWeb());
  myservlet.setName("myServlet");
  //把servlet加入到contxt中
  context.addChild(myservlet);
//这里注意,要先添加到contxt中在映射路径,不然会空指针
  myservlet.addMapping("/chyweb1");

最后一步启动tomcat

//tomcat启动
tomcat.start();
//保持主线程不退出
tomcat.getServer().await();

测试浏览器输入

http://127.0.0.1:8888/chyweb1

到这里最简单的demo完成.

5.静态文件的加载

上面的demo只能处理一个自定义的servlet.而且不能处理jsp以及静态文件.

现在在项目路径下有一个 test.html的静态文件.按照以前tomcat的使用习惯,我在浏览器输入

http://127.0.0.1:8888/test.html

应该可以加载出这个html文件的对应信息,结果出现的是404.

如果熟悉tomcat可以知道,tomcat有个默认的servlet用来处理静态请求.还有一个专门处理jsp的servlet

通过源码分析,发现他还是提供了加载默认的servlet

 context.addLifecycleListener(tomcat.getDefaultWebXmlListener());

使用上面这个API可以在初始化的时候加载上面标注出来的方法.

因为现在项目都在逐渐去除JSP,使用前后端分离,所以我这里就手动添加默认的servlet,而不引入jsp的servlet

//创建一个默认的Servlet 用来处理 html 等静态资源,如果上面设置了getDefaultWebXmlListener 可以不添加
        Wrapper defaultServlet = new StandardWrapper();
        defaultServlet.setServlet(new DefaultServlet());
        defaultServlet.setName("default");
        context.addChild(defaultServlet);
        defaultServlet.addMapping("/");

添加该servlet后重启项目,再次运行

http://127.0.0.1:8888/test.html

6. web.xml加载

这里如果使用手动添加servlet的方式,每次都很麻烦,而且代码耦合性太大,所以想能不能去像以前一样读web.xml文件.然后加载对应的servlet

通过源码分析发现加入以下配置即可

//初始化ContextConfig配置
context.addLifecycleListener(new ContextConfig());

但是只能在项目路径下的 WEB-INF/web.xml 文件

嗯果然还是tomcat的老套路,但是我就想随意定义web.xml怎么办

博主又在源码中找找找发现

从这段代码中发现,那我只要给他设上值不就可以了吗.

so加入

        context.getServletContext().setAttribute("org.apache.catalina.deploy.alt_dd",
"/Users/bignosecat/IdeaProjects/xx/tomcat/src/main/resources/web.xml");

然后我访问了这个web.xml中定义的路径,成功

7. 完整的嵌入Tomcat 启动代码

 Tomcat tomcat = new Tomcat();
        //Server server = tomcat.getServer();

        //看源码发现,他只能设置一个service,直接拿默认的
        Service service = tomcat.getService();

        //创建连接器,并且添加对应的连接器,同时连接器指定端口
        Connector connector = new Connector();
        connector.setPort(8888);
        service.addConnector(connector);

        //创建一个引擎,放入service中
        Engine engine = new StandardEngine();
        service.setContainer(engine);
        engine.setDefaultHost("localhost");
        engine.setName("myTomcat");

        //添加host
        Host host = new StandardHost();
        engine.addChild(host);
        host.setName("localhost");
        host.setAppBase("webapps");


        //在对应的host下面创建一个 context 并制定他的工作路径
       StandardContext context = (StandardContext) tomcat.addContext(host, "/", "/Users/bignosecat/IdeaProjects/xx/tomcat/target");

       //初始化ContextConfig配置
        context.addLifecycleListener(new ContextConfig());
        
        //加载指定位置的web.xml
        //context.getServletContext().setAttribute("org.apache.catalina.deploy.alt_dd","/Users/bignosecat/IdeaProjects/xx/tomcat/src/main/resources/web.xml");

        //初始化 默认的servlet,这里会 触发tomcat#initWebappDefaults 方法,等价于平时tomcat自动加载默认的conf/web.xml文件
        //当然也可以手动加载,这里因为我没引用jsp的包,所以不自动加载.
        context.addLifecycleListener(tomcat.getDefaultWebXmlListener());

        //创建一个默认的Servlet 用来处理 html 等静态资源,如果上面设置了getDefaultWebXmlListener 可以不添加
        Wrapper defaultServlet = new StandardWrapper();
        defaultServlet.setServlet(new DefaultServlet());
        defaultServlet.setName("default");
        context.addChild(defaultServlet);
        defaultServlet.addMapping("/");


        //创建一个servlet
        Wrapper myservlet = new StandardWrapper();
        myservlet.setServlet(new MyWeb());
        myservlet.setName("myServlet");
        context.addChild(myservlet);
        myservlet.addMapping("/chyweb1");
        
        //tomcat启动
        tomcat.start();
        //保持主线程不退出
        tomcat.getServer().await();

8. 整合springmvc

O__O "…,这个还需要整合吗.直接把springmvc的web.xml 加载进嵌入tomcat 就可以了.

9.嵌入tomcat封装

这里博主把这API封装了一道,用于方便调用.代码具体看下面github,下面只演示API的使用

github
gitee​​​​​​​

下面是这个框架的使用介绍

测试项目路径如下(一个最简单的springmvc项目):

 

在github获取项目后,安装进maven 引入坐标

       <dependency>
            <groupId>chy.frame</groupId>
            <artifactId>tomcat</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

在Main中写入然后直接运行即可

  public static void main(String[] args) {
        
               /**
         *  构造有3个参数
         *  1. tomcat的端口
         *  2. web.xml的路径
         *  3. 项目静态文件的路径
         * 路径可以写 绝对路径或者
         *  classpath: 开头 在classes路径下寻找
         *  projectpath: 开头 在项目路径下寻找
         *  
         */ 
        ChyTomcat chyTomcat = new ChyTomcat(8001,"classpath:web.xml",
                "projectpath:static");
        //服务开启
        chyTomcat.start();
    }

成功启动tomcat,并访问成功所定义的请求

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值