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();
测试浏览器输入
到这里最简单的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的使用
下面是这个框架的使用介绍
测试项目路径如下(一个最简单的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,并访问成功所定义的请求