小马哥训练营-Java EE单体架构

什么是Servlet

Servlet 是一种基于 Java 技术的 Web 组件,用于生成动态内容,由容器管理。类似于其他 Java 技术组件,Servlet 是平台无关的 Java 类组成,并且由 Java Web 服务器加载执行。通常情况,由 Servlet 容器提供运行时环境。Servlet 容器,有时候也称作为Servlet 引擎,作为Web服务器或应用服务器的一部分。通过请求和响应对话,提供Web客户端与 Servlets 交互的能力。容器管理Servlets实例以及它们的生命周期。
相关技术:
CGI - 基于进程,php, apache httpd CGI
Servlet-基于线程

servlet主要版本

规范版本 发布时间 Java平台 主要更新
Servlet4.0 2017 JavaEE8 支持http/2
Servlet3.1 2013 JavaEE7 非阻塞I/O,http协议更新机制(websoket)
Servlet3.0 2009 JavaEE6 可插拔、简化部署、异步servlet,安全,文件上传
Servlet2.5 2005 JavaEE5 Annotation支持
Servlet2.4 2003 J2EE1.4 web.xml支持xml scheme
Servlet2.3 2001 J2EE1.3 新增filter,事件/监听器,Wrapper
Servlet2.2 1999 J2EE1.2 作为J2ee的一部分,以.war文件作为独立web应用

补充知识:
zip,jar,war,ear 其本质都是zip的拓展,都是压缩文件。war和jar拓展名不一样内容是一样的。

Servlet 核心API

核心组件API 说明 起始版本 SpringFrameWork代表实现
javax.servlet .Servlet 动态内容组件 1.0 DispatcherServlet
javax.servlet.Filter Servlet过滤器 2.3 CharacterEncodingFilter
javax.servlet .ServletContext Servlet 应用上下文
javax.servlet .AsyncContext 异步上下文 3.0
javax.servlet.ServletContextListener ServletContext 生命周期监听器 2.3 ContextLoaderListener
javax.servlet.ServletRequestListener ServletRequest 生命周期监听器 2.3 RequestContextListener
javaxservlet.http.HttpSessionListener HttpSession 生命周期监听器 2.3 HttpSessionMutexListener
javax.servlet.AsyncListener 异步上下文监听器 3.0 StandardServletAsyncWebRequest
javax.servlet.ServletContainerInitializer Servlet 容器初始化器 3.0 Springservletcoptelyong

Servlet 组件注册方式

  • 传统web.xml注册方式 (写xml)
  • 注解方式(servlet 3.0 +)注解@WebServlet
  • 编码注册方式(servlet 3.0 +)
    在这里插入图片描述
    注册完成以后返回句柄,可以添加映射:
    在这里插入图片描述

Servlet 生命周期

  • 声明(应用行为)
  • 注册(容器行为 )
  • 初始化: Servlet#initServletConfig)
  • 服务: Servlet#service(ServletRequest, ServletResponse)
  • 销毁: Servlet#destroy()

Filter生命周期

  • 声明(应用行为)
  • 注册(容器行为)
  • 初始化:Filter#init(FilterConfig)
  • 过滤:Filter#doFilter(ServletRequest,ServletResponse,FilterChain)
  • 毁:Filter#destroy()

ServletContext 生命周期

  • 声明(应用行为)
  • 注册(容器行为)
  • 初始化:ServletContextListener#contextInitialized
  • 销毁:ServletContextListener#contextDestroyed

注:
Servlet规范重点章节 2,3,4,5,9,11,12 规范链接
CHAPTER 2 The Servlet Interface
CHAPTER 3 The Request
CHAPTER 4 Servlet Context
CHAPTER 5 The Response
CHAPTER 9 Dispatching Requests(非常重要)
CHAPTER 11Application Lifecycle Events
CHAPTER 12 Mapping Requests to Servlets(非常重要)
还需要看TomcatNIO
并非请求头(header)和请求体(body)都是异步读取的,要看Servlet容器的实现模型。
Tomcat官方文档
在这里插入图片描述

关于Tomcat 的一个优化

目前我们都不使用jsp了所以我们可以将jsp的servlet关掉。在tomcat中将此Servlet注释掉。

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>

关于Tomcat处理静态文件,使用了默认的servlet 来处理

因为jspServerlet 只是匹配jsp文件,要匹配静态文件我们需要用DefaultServlet来匹配。各个服务平台往往都是用default来命名,在springboot里静态文件的处理是forward(这个就是转发请求到下一个servlet) 到对应平台的Servlet来处理,从而适配了。所以在springboot 里面将css 和js文件放到static文件下面就可以读取到。
源码:

public class DefaultServletHttpRequestHandler implements HttpRequestHandler, ServletContextAware {
	// 这里定义了不同平台处理静态文件的servlet 名称
    private static final String COMMON_DEFAULT_SERVLET_NAME = "default";
    private static final String GAE_DEFAULT_SERVLET_NAME = "_ah_default";
    private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";
    private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";
    private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet";
    @Nullable
    private String defaultServletName;
    @Nullable
    private ServletContext servletContext;
    public DefaultServletHttpRequestHandler() {
    }
    public void setDefaultServletName(String defaultServletName) {
        this.defaultServletName = defaultServletName;
    }
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
        if (!StringUtils.hasText(this.defaultServletName)) {
            if (this.servletContext.getNamedDispatcher("default") != null) {
                this.defaultServletName = "default";
            } else if (this.servletContext.getNamedDispatcher("_ah_default") != null) {
                this.defaultServletName = "_ah_default";
            } else if (this.servletContext.getNamedDispatcher("resin-file") != null) {
                this.defaultServletName = "resin-file";
            } else if (this.servletContext.getNamedDispatcher("FileServlet") != null) {
                this.defaultServletName = "FileServlet";
            } else {
                if (this.servletContext.getNamedDispatcher("SimpleFileServlet") == null) {
                    throw new IllegalStateException("Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.");
                }
                this.defaultServletName = "SimpleFileServlet";
            }
        }
    }
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Assert.state(this.servletContext != null, "No ServletContext set");
        RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
        if (rd == null) {
            throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" + this.defaultServletName + "'");
        } else {
        	// 分发到对应的servlet 进行处理 因为他们都遵循了同一个规范
            rd.forward(request, response);
        }
    }
}

关于default servlet 处理静态文件的一个配置示例

<!--映射路径-->
<servlet-mapping>
     <servlet-name>default</servlet-name>
     <url-pattern>/</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
     <servlet-name>default</servlet-name>
     <url-pattern>*.css</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
     <servlet-name>default</servlet-name>
     <url-pattern>*.js</url-pattern>
 </servlet-mapping>
      <servlet>
       <servlet-name>default</servlet-name>
       <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
       <init-param>
           <param-name>debug</param-name>
           <param-value>0</param-value>
       </init-param>
       <init-param>
           <param-name>listings</param-name>
           <param-value>false</param-value>
       </init-param>
       <load-on-startup>1</load-on-startup>
   </servlet>

注解:
关于forward 的时候会不会经过过滤链,这需要在过滤链进行配置。关于执行顺序,先定义mapping的filter优先。

<filter-mapping>
    <filter-name>CharsetEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <!-- 配置 各种操作的时候也经过过滤器-->
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>

课堂上的补充知识点:
SPI:spi加载机制

关于打包

使用tomcat打包插件将项目打包为可执行的jar文件

<plugin>
  <groupId>org.apache.tomcat.maven</groupId>
  <artifactId>tomcat7-maven-plugin</artifactId>
  <version>2.1</version>
  <executions>
    <execution>
      <id>tomcat-run</id>
      <goals>
        <!-- 最终打包成可执行的jar包 -->
        <goal>exec-war-only</goal>
      </goals>
      <phase>package</phase>
      <configuration>
        <!-- ServletContext 路径 -->
        <path>/</path>
        <enableNaming>true</enableNaming>
        <tomcatConfigurationFilesDirectory>
          src/main/webapp/META-INF/conf/
        </tomcatConfigurationFilesDirectory>
      </configuration>
    </execution>
  </executions>
</plugin>

顺便也补充一个web.xml,关于映射路径规则可以详情见12章,同时补充/ 与/星号的区别,/星号如果配置了就会匹配所有的路径,所有的请求都会走这个Servlet, 所以/星号一般是配在过滤器上。/与/*的区别

<?xml version="1.0" encoding="GBK"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         metadata-complete="true" version="3.0">
  <servlet>
    <servlet-name>FrontControllerServlet</servlet-name>
    <servlet-class>FrontControllerServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!--处理请求的Servlet 在SpringMvc里面是DispatchServlet-->
  <servlet-mapping>
    <servlet-name>FrontControllerServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  <display-name>Archetype Created Web Application</display-name>
  <filter>
    <filter-name>CharsetEncodingFilter</filter-name>
    <filter-class>filter.CharsetEncodingFilter</filter-class>
    <init-param>
      <!-- Filter 配置 = FilterConfig -->
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>CharsetEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>
  <servlet>
    <servlet-name>default</servlet-name>
    <!--处理静态文件的servlet-->
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
      <param-name>debug</param-name>
      <param-value>0</param-value>
    </init-param>
    <init-param>
      <param-name>listings</param-name>
      <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
  </servlet-mapping>
</web-app>

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>