Servlet入门

1. Servlet概述

1.1 什么是 Servlet

Servlet 是基于 Jakarta 技术的 Web 组件,由容器管理,可生成动态内容。与其他基于 Jakarta 技术的组件一样,servlet 是独立于平台的 Java 类,它们被编译为与平台无关的字节码,这些字节码可以动态加载到支持 Jakarta 技术的 Web 服务器中并由其运行。容器,有时也称为 servlet 引擎,是提供 servlet 功能的 Web 服务器扩展。Servlet 通过 servlet 容器实现的请求/响应范式与 Web 客户端交互。

1.2 什么是 Servlet 容器

Servlet 容器是 Web 服务器或应用程序服务器的一部分,它提供发送请求和响应的网络服务、解码基于 MIME 的请求以及格式化基于 MIME 的响应。Servlet 容器还通过其生命周期包含和管理 Servlet

Servlet 容器可以内置到主机 Web 服务器中,也可以通过该服务器的本机扩展 API 作为附加组件安装到 Web 服务器。Servlet 容器也可以内置于或可能安装在支持 Web 的应用程序服务器中。

​ 所有 Servlet 容器都必须支持 HTTP 作为请求和响应的协议,但可以支持其他基于请求/响应的协议,例如 HTTPS(基于 SSLHTTP)。容器必须实现的 HTTP 规范的必需版本是 HTTP/1.1HTTP/2

​ Java SE 8 是必须用来构建 Servlet 容器的底层 Java 平台的最低版本。

1.3 一个例子

以下是一个典型的事件序列:

  1. 客户端(例如,Web 浏览器)访问 Web 服务器并发出 HTTP 请求。
  2. 请求由 Web 服务器接收并传递给 Servlet 容器。Servlet 容器可以在与主机 Web 服务器相同的进程中运行,也可以在同一主机上的不同进程中运行,或者在与其处理请求的 Web 服务器不同的主机上运行。
  3. Servlet 容器根据其Servlet 的配置确定调用哪个 Servlet,并使用代表请求和响应的对象调用它。
  4. Servlet 使用请求对象来找出远程用户是谁、POST作为此请求的一部分发送的HTTP参数以及其他相关数据。Servlet 执行它编程的任何逻辑,并生成数据发送回客户端。它通过响应对象将此数据发送回客户端。
  5. Servlet 处理完请求后,Servlet 容器会确保正确刷新响应,并将控制权返回给主机 Web 服务器。

2. Servlet核心技术

2.1 Servlet加载时机

在默认情况下,当Web客户第一次请求访问某个Servlet时,Web容器会创建这个Servlet的实例。当设置了web.xml中的子元素后,Servlet容器在启动Web应用时,将按照指定顺序创建并初始化这个Servlet。设置的数值大于0即可。例如:

<servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.ydlclass.servlet.HelloServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

2.2 Servlet的生命周期

先看与Servlet生命周期有关的三个方法:init(), service(), destroy(). Servlet生命周期可被定义为从创建直到毁灭的整个过程。以下是三个方法分别对应的Servlet过程:

  1. void init(ServletConfig argo):执行时机,当Servlet实例被创建时候调用,做初始化工作。ServletConfig的配置对象,初始化的时候可以进行配置。
  2. void service(ServletRequest arg0,ServletResponse arg1):执行时机,当一个请求来请求当前的Servlet的时候被调用,处理当前的Servlet的业务逻辑并把响应返回给浏览器。
  3. void destroy():执行时机,是Servlet的实例对象被销毁的时候调用,做一些收尾或清理工作。

在调用destroy()方法后,ServletJVM的垃圾回首器进行垃圾回收。

还有一些其他方法:

  1. String getServletinfo():获得当前的一些属性信息。
  2. ServletConfig getServletConfig():获得ServletConfig的配置对象

现在我们来详细讨论Servlet生命周期的方法:

init()方法

Servlet被装载后,Servlet容器创建一个Servlet实例并且调用Servletinit()方法进行初始化在Servlet生命周期中init()方法只被调用一次

当用户调用一个Servlet时,Servlet容器就会创建一个Servlet实例,每一个用户请求都会产生一个新的线程,init()方法简单的创建或加载一些数据,这些数据将会被用在Servlet的整个生命周期。

init()方法的定义如下:

public void init() throws ServletException {
  // 初始化代码...
}

service()方法

service()方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service()方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。

每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service()方法检查 HTTP 请求类型(GETPOSTPUTDELETE 等),并在适当的时候调用doGet()doPost()等方法。

service()的定义如下:

public void service(ServletRequest request, ServletResponse response) 
    throws ServletException, IOException{
// service()代码...
}

destroy()方法

destroy()方法只会被调用一次,在Servlet生命周期结束时被调用。destroy()方法可以让Servlet关闭数据库连接、停止后台、把cookie列表或点击计数器写入到磁盘,并执行其他类似的清理活动。 在调用destroy()方法之后,Servlet对象被标记为垃圾回收。

destroy()方法的定义如下所示:

public void destroy() {
    // 终止化代码...
  }

总结

  • 在首次访问某个Servlet时,init()方法会被执行,而且也会执行service()方法。
  • 再次访问时,只会执行service()方法,不再执行init()方法。
  • 在关闭Web容器时会调用destroy()方法。

2.3 实现一个Servlet

当服务器接收到一个请求,就要有一个Servlet去处理这个请求,所以完成一个Servlet通常需要两步走。

  • 一方面要写一个java程序定义一个Servlet
  • 另一方面要配置一下Servlet确定这个Servlet要处理哪一个请求。

创建Servlet的三种方式

  • 实现javax.servlet.Servlet接口。
  • 继承javax.servlet.GenericServlet类。
  • 继承javax.servlet.http.HttpServlet类。

我们在日常开发中一般会使用第三种方法来进行Servlet的创建,前两种方法理解即可。

注意:创建Servlet文件后,需要在web.xml文件中完成Servlet配置,才可以使用。

Servlet

通过实现Servlet接口,这个接口定义了servlet的生命周期,所有的方法需要我们实现。

public class UserServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        servletResponse.getWriter().print("

hello servlet

"
); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }

GenericServlet

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final long serialVersionUID = 1L;
    private transient ServletConfig config;

    public GenericServlet() {
    }

    public void destroy() {
    }

    public String getServletInfo() {
        return "";
    }
    
    public void init() throws ServletException {
    }

    public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    public String getServletName() {
        return this.config.getServletName();
    }
    
    ....
}
public class UserServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        servletResponse.getWriter().print("

hello servlet

"
); } }

Http只是会根据请求的类型进行特殊的调用

package javax.servlet.http;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.ResourceBundle;
import javax.servlet.DispatcherType;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public abstract class HttpServlet extends GenericServlet {


    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }

    }

    protected long getLastModified(HttpServletRequest req) {
        return -1L;
    }



    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);
        }

    }

	// 还是会调用它,只是会根据请求的类型进行特殊的调用
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }
    
    ...

}
  • HttpServletRequestServletRequest都是接口
  • HttpServletRequest继承自ServletRequest
  • HttpServletRequestServletRequest多了一些针对于Http协议的方法。 例如:getHeader()getMethod()getSession()

HttpServlet

public class HttpServletDemo extends HttpServlet {


	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("doGet方法被调用");
		resp.getOutputStream().write("hello Loanon".getBytes());
	}



	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("doPost方法被调用");
		doGet(req, resp);
	}
}

3. Servlet的匹配规则

配置web.xml

<servlet>
    <servlet-name>helloServletservlet-name>//随便写,但是要和下面的对应一样
    <servlet-class>com.r1.servlet.ServletDemoservlet-class>//具体的类路径
  servlet>
  <servlet-mapping>
    <servlet-name>helloServletservlet-name>//和上面的名字一样
    <url-pattern>/hellourl-pattern>//浏览器输入的域名
  servlet-mapping>

3.1 四种匹配规则

精确匹配

中配置的项必须与url完全精确匹配。

<servlet-mapping>
    <servlet-name>MyServletservlet-name>
    <url-pattern>/user/users.htmlurl-pattern>
    <url-pattern>/index.htmlurl-pattern>
    <url-pattern>/user/addUserurl-pattern>
servlet-mapping>

当在浏览器中输入如下几种url时,都会被匹配到该servlet

  1. http://localhost:8080/appDemo/user/users.html
  2. http://localhost:8080/appDemo/index.html
  3. http://localhost:8080/appDemo/user/addUser

注意:

http://localhost:8080/appDemo/user/addUser?username=Tom&age=23也会被匹配到MyServlet

路径匹配

以“/”字符开头,并以“/*”结尾的字符串用于路径匹配

<servlet-mapping>
    <servlet-name>MyServletservlet-name>
    <url-pattern>/user/*url-pattern>
servlet-mapping>

路径以/user/开始,后面的路径可以任意。比如下面的url都会被匹配。

  • http://localhost:8080/appDemo/user/users.html
  • http://localhost:8080/appDemo/user/addUser
  • http://localhost:8080/appDemo/user/bb//sdf/sdf/sdf/updateUser

扩展名匹配

以“*.”开头的字符串被用于扩展名匹配

<servlet-mapping>
    <servlet-name>MyServletservlet-name>
    <url-pattern>*.jspurl-pattern>
    <url-pattern>*.dourl-pattern>
servlet-mapping>

则任何扩展名为jspactionurl请求都会匹配,比如下面的url都会被匹配

  • http://localhost:8080/appDemo/user/users.jsp
  • http://localhost:8080/appDemo/toHome.action

缺省匹配

<servlet-mapping>
    <servlet-name>MyServletservlet-name>
    <url-pattern>/url-pattern>
servlet-mapping>

3.2 匹配顺序

  1. 精确匹配。
  2. 路径匹配,先最长路径匹配,再最短路径匹配。
  3. 扩展名匹配。

注意:使用扩展名匹配,前面就不能有任何的路径。
4. 缺省匹配,以上都找不到Servlet,就用默认的Servlet,配置为/

3.3 需要注意的问题

路径匹配和扩展名匹配无法同时设置

匹配方法只有三种 :

  • 要么是路径匹配(以“/”字符开头,并以“/*”结尾)。
  • 要么是扩展名匹配(以“*.”开头)
  • 要么是精确匹配

三种匹配方法不能进行组合,不要想当然使用通配符。

  • /user/*.action是非法的
  • 另外注意:/aa/*/bb是精确匹配,合法,这里的*不是通配的含义

"/*“和”/"含义并不相同

  • /*”属于路径匹配,并且可以匹配所有request,由于路径匹配的优先级仅次于精确匹配,所以“/*”会覆盖所有的扩展名匹配,很多404错误均由此引起,所以这是一种特别恶劣的匹配模式。
  • /”是servlet中特殊的匹配模式,切该模式有且仅有一个实例,优先级最低,不会覆盖其他任何url-pattern,只是会替换servlet容器的内建default servlet ,该模式同样会匹配所有request

Tomcat%CATALINA_HOME%\conf\web.xml文件中配置了默认的Servlet,配置代码如下:

<servlet>
        <servlet-name>defaultservlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServletservlet-class>
        <init-param>
            <param-name>debugparam-name>
            <param-value>0param-value>
        init-param>
        <init-param>
            <param-name>listingsparam-name>
            <param-value>falseparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>
<servlet>
        <servlet-name>jspservlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServletservlet-class>
        <init-param>
            <param-name>forkparam-name>
            <param-value>falseparam-value>
        init-param>
        <init-param>
            <param-name>xpoweredByparam-name>
            <param-value>falseparam-value>
        init-param>
        <load-on-startup>3load-on-startup>
 servlet>
<servlet-mapping>
        <servlet-name>defaultservlet-name>
        <url-pattern>/url-pattern>
servlet-mapping>

    
<servlet-mapping>
        <servlet-name>jspservlet-name>
        <url-pattern>*.jspurl-pattern>
        <url-pattern>*.jspxurl-pattern>
servlet-mapping>

/*”和“/”均会拦截静态资源的加载,需要特别注意

你可能感兴趣的:(spring进阶,spring,servlet,java,服务器)