Java EE: Tomcat

Tomcat

依赖

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>11.0.10</version>
</dependency>

<!-- 若用到jsp技术 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>11.0.10</version>
</dependency>

结构介绍

  • TomcatApache开发的开源Servlet容器

  • 其中,WEB-INF目录是受保护的,客户端无法访问这个目录下的资源,保存应用的信息

    这个目录下面还有classes/lib/以及web.xml

    classes/存放Java程序的编译结果、lib/存放一系列第三方jar

    其余应是一系列静态资源,例如js/css/views/等常见的命名

    使用maven管理项目的情况下,lib/不需要手动寻找、配置

  • 传统部署:指Tomcat服务器包含开发者开发的.war包这样的Web应用,Servlet应用开发好后打包成.war包并复制到Tomcat服务器中,然后启动服务器

    传统部署中,classes/lib/Tomcat解包.war文件得到

  • 内嵌tomcat:指Tomcat API作为java程序的一部分而存在,若使用maven的情况下调用Tomcat实例的addWebapp()方法,实例会从指定的路径中找到WEB-INF/web.xml文件并解析,然后生成classes/lib/等在target/

web.xml映射

  • 这是一种比较传统的映射方式,仅了解即可

  • web.xml应位于webapp/WEB-INF目录下,其中webapp为自定义的网页应用的名字,web.xml里的所有的配置会覆盖注解形式的映射

  • 声明为webapp

    1
    2
    3
    4
    5
    6
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" id="WebApp_ID" version="4.0">

    </web-app>
  • 声明一个Servlet

    1
    2
    3
    4
    <servlet>
    <servlet-name>Servlet_name</servlet-name>
    <servlet-class>类路径</servlet-class>
    </servlet>
  • Servlet映射到某个路径上:

    1
    2
    3
    4
    <servlet-mapping>
    <servlet-name>Servlet_name</servlet-name>
    <url-pattern>/路径</url-pattern>
    </servlet-mapping>
  • 初始化某个Servlet或某个Filter的参数(ServletConfig):

    1
    2
    3
    4
    <init-param>
    <param-name>param_name</param-name>
    <param-value>xml文件、字符串等</param-value>
    </init-param>
  • 设置ServletContext的配置:

    1
    2
    3
    4
    <context-param>
    <param-name>name</param-name>
    <param-value>value</param-value>
    </context-param>
  • 设置欢迎文件:

    1
    2
    3
    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    </welcome-file-list>
  • 设置session的生效时间

    1
    2
    3
    <session-config>
    <session-timeout>30</session-timeout>
    </session-config>

注解映射

  • @WebServlet注解:在Servlet 3.0前,需要在web.xml中配置大量信息,在WebServlet注解出现后,所有的配置被分散到各个服务类上,最重要且必要的属性是urlPatterns,是String集合,表示这些URI均映射到这个Servlet类上

    在传统部署时,它们的解析是全自动的

内嵌Tomcat

  • 内嵌部署Tomcat显得更加灵活,由开发者完全控制Tomcat的配置,但因此注解映射、静态资源需要由开发者亲自解析、引入,因此在不借用Spring boot框架的情况下,直接配置web.xml并调用addWebapp()或手动通过Tomcat对象注册服务以及配置映射是更常用的

  • 可以理解为:通过web.xml配置的底层实现就是让addWebapp()解析该xml文件,并间接地在内部调用addServlet()addMapping()等方法

  • new Tomcat()直接构造一个Tomcat实例,调用start()启动该服务器(会抛出LifecycleException受检异常),调用getServer().await()使该服务器不断地等待请求

  • Tomcat实例方法:

    • setHostname(String):设置服务器地址

    • setPort(int):设置服务器端口

    • setBaseDir(String):设置基准目录,表示Tomcat实例工作时,临时文件等存放的根目录,这里的临时文件不是classes/lib/等,而是运行时生成的文件,例如jsp翻译后生成的文件

      推荐设置为target/下的某个目录

    • getConnector():获取其绑定的连接器,这个方法在首次调用时会进行一系列的初始化操作,包括应用在此前设置的baseDir等参数

      这个方法应尽量晚些调用

    • Context addWebapp(String contextPath, String docBase):添加Web应用并获取它的上下文实例

      contextPath指上下文的根所映射的URL路径,例如传入""则该上下文会映射到"/"

      docBase指要引入的资源的本地路径,提供的路径下必须含有WEB-INF目录及web.xml文件

      等价于将整个Web应用加载到服务器上,使用addWebapp()会自动生成并注册一个默认的Servlet服务,当客户端访问那些在用户自定义Servlet服务中找不到的服务时,会转向这个默认服务

      默认服务会试图在docBase下的静态资源中查询是否有对应的资源,若找到则返回,否则返回404响应

    • Context addContext(String contextPath, String docBase):添加一个空白的上下文实例,参数意义同上

    • addServlet(String contextPath, String servletName, Servlet serv):登记一个名为servletName的微服务,对应到路径为contextPath的上下文

      有静态方法的版本,其中contextPath应替换为Context对象

      同时需要使用Context实例的addServletMappingDecoded(String pattern, String name)将微服务映射到URL的某个路径上

      这两步等价于在web.xml中声明Servlet服务和映射

      该方法返回的Wrapper对象可链式调用addMapping(String pattern),它等价于使用Context实例的addServletMappingDecoded()方法

  • 上下文类Context抽象的是整个Web容器,和ServletContext不同,一般作用于容器启动前,而后者作用于Web应用运行中处理业务,Context常用的实例方法有:

    • addWelcomeFile(String):设置当客户端访问根路径时,返回的资源文件

      即将String指向的资源文件映射到"/"

    • addServletMappingDecoded(String pattern, String name):将微服务映射到某个URL

    • addFilterDef(FilterDef):登记一个Filter,由FilterDef封装其信息

      FilterDef常用方法:setFilter(Filter)setFilterName(String)addInitParameter(String, Object)

      分别为登记Filter对象、登记Filter服务名、初始化Filter参数

    • addFilterMap(FilterMap):登记从FilterURL的映射,由FilterMap封装映射信息

      FilterMap常用方法:addURLPatternDecoded(String)addServletName(String)setFilterName(String)

      分别为登记其应用的URL、登记其应用的Servlet、设置其绑定的Filter服务名

例子

  • 基本配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Tomcat tomcat = new Tomcat();

    tomcat.setHostname("localhost");
    tomcat.setPort(8080);
    tomcat.setBaseDir("/target/tomcat/");
    //
    // ...其它配置...
    //
    tomcat.getConnector();
    tomcat.init();
    tomcat.start();
    tomcat.getServer().await();
  • Servlet等配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    String contextPath = "/api";
    // String docBase = new File("/src/main/webapp").getAbsolutePath();
    String docBase = Path.of("/src/main/webapp").toAbsolutePath().toString();
    Context context = tomcat.addContext(contextPath, docBase);

    String servletName = "ServletDemo";
    tomcat.addServlet(contextPath, servletName, new ServletDemo()).addMapping("/*");
    // context.addServletMappingDecoded("/*", servletName);

    FilterDef filterDef = new FilterDef();
    filterDef.setFilter((req, resp, chain) -> {
    // 过滤 ...
    // 链式处理
    chain.doFilter(req, resp);
    });
    filterDef.setFilterName("FilterDemo");

    FilterMap filterMap = new FilterMap();
    filterMap.addURLPatternDecoded("/*");
    filterMap.addServletName(servletName);
    filterMap.setFilterName("FilterDemo");

    context.addFilterDef(filterDef);
    context.addFilterMap(filterMap);
  • 上述配置与以下配置是近乎等价的,只是以下配置会有多出一个默认的Servlet服务,用于检索静态资源

    1
    tomcat.addWebapp("/api", "src/main/webapp/");
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <!-- web.xml -->
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    id="WebApp_ID" version="4.0">

    <servlet>
    <servlet-name>ServletDemo</servlet-name>
    <servlet-class>demo.servlet.ServletDemo</servlet-class>
    </servlet>

    <servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>/*</url-pattern>
    </servlet-mapping>

    <filter>
    <filter-name>FilterDemo</filter-name>
    <filter-class>无法用lambda表达式声明的接口实现</filter-class>
    </filter>

    <filter-mapping>
    <filter-name>FilterDemo</filter-name>
    <!-- 针对/*这个URL路径设置Filter -->
    <url-pattern>/*</url-pattern>
    <!-- 针对ServletDemo这个服务单独设置Filter -->
    <servlet-name>ServletDemo</servlet-name>
    </filter-mapping>

    </web-app>