Servlet中几个常用方法的推衍

  JavaWeb开发中,Servlet是重要的一环,常用于处理逻辑事物。现在比较习惯用编辑器直接新建一个Servlet出来,但是对于初学者,这样的方式并不能理解到它从最原始的形态到现在的演变,本文就此做一个简单的推演,从Servlet的生命周期开始,到现在比较常用的做法,搭建一个桥梁,让初学者可以很容易看懂为什么要这么做。

  新建一个Maven工程,引入必要依赖:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.0</version>
    <scope>provided</scope>
</dependency>

在web.xml中配置好<Servlet/>以及<Servlet-mapping/>

<servlet>
    <servlet-name>OriginalServlet</servlet-name>
    <servlet-class>com.test.OriginalServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>OriginalServlet</servlet-name>
    <url-pattern>/OriginalServlet</url-pattern>
</servlet-mapping>

并在类中,实现Servlet接口,实现其中的几个方法:

import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * Servlet implementation class OriginalServlet
 */
public class OriginalServlet implements Servlet {

    /**
     * Default constructor.
     */
    public OriginalServlet() {
        // TODO Auto-generated constructor stub
    }

    /**
     * @see Servlet#init(ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub
    }

    /**
     * @see Servlet#destroy()
     */
    public void destroy() {
        // TODO Auto-generated method stub
    }

    /**
     * @see Servlet#service(ServletRequest request, ServletResponse response)
     */
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

    /**
     * @see Servlet#getServletConfig()
     */
    public ServletConfig getServletConfig() {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * @see Servlet#getServletInfo()
     */
    public String getServletInfo() {
        // TODO Auto-generated method stub
        return null;
    }
}

  诠释Servlet生命周期时,一定离不开三个重要的方法:

  • 一个init(ServletConfig),表示初始化方法。默认情况下,是第一次调用该Servlet之前执行,如果需要它在服务器启动时执行,只需要在web.xml中配置<load-on-startup/>参数,其中的数字越小,被加载的优先级越高;

ServletConfig表示servlet配置信息的封装对象,其中有几个比较重要的方法:

getServletName()   --  获得<servlet/><servlet-name/>的名称
getInitParameter(name)   --  获得初始化参数
getInitParameterNames()  --  获得所有的初始化参数(迭代器前身)
getServletContext()  --  详细内容见后部分

  • 第二个是service(ServletRequest,ServletResponse)表示servlet执行方法,每一次请求将会执行一次;

tomcat作为服务器时,每次访问到该Servlet时,就会调用该service方法,控制台输出该request与response参数,可以发现输出如下:

org.apache.catalina.connector.RequestFacade@5caf2b5
org.apache.catalina.connector.ResponseFacade@624641db

翻看源码,可以看出,这两个对象所对应的类,其实所实现的是HttpServletRequest与HttpServletResponse接口,所以:

HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
  • 第三个是destroy()表示销毁方法,会在服务器正常关闭时执行一次。

------------------------------------------------------------------------------------->

  有了上述的接口的几个方法,我们会想到,如何在真正使用的时候,将它最简化。对于init(ServletConfig),destroy()等不常用到的方法,我们可以用一个基础类实现Servlet接口,之后所有的servlet类继承该基础类,需要用到某个方法时,覆写实现即可。

  以下几点都是基于如何将后续常用的Servlet最简化而进行的基础工作:

  • 将service方法用abstract修饰,使这个方法成为子类必须实现的方法
  • 基础类中的init(ServletConfig)方法拥有的配置信息对象参数,要用一种简洁方式提供给子类,所以用私有属性获得该参数,并提供get方法返回;进一步地,如果子类中需要用到该参数,那么会getServletConfig().getServletName()获取,于是我们让基础类实现ServletConfig这个接口,提供几个获取重要参数的方法,并在方法体中返回getServletConfig().getXXX(),这样,子类中就可以直接调用这些方法获取到相应的参数
  • 避免出现子类的init(ServletConfig)方法忘记执行super(ServletConfig)而出现NullPointerException,所以在基础类中的init(ServletConfig)方法调用一个本地的init()方法,子类如果需要,就覆盖init()方法,保证初始化顺利地进行
package javax.servlet;

import java.io.IOException;
import java.util.Enumeration;
import java.util.ResourceBundle;

public abstract class GenericServlet 
    implements Servlet, ServletConfig, java.io.Serializable
{
    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);

    private transient ServletConfig config;


    public GenericServlet() { }


    public void destroy() {
    }

    public String getInitParameter(String name) {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getInitParameter(name);
    }

    public Enumeration getInitParameterNames() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getInitParameterNames();
    }   
    
    public ServletConfig getServletConfig() {
    return config;
    }

    public ServletContext getServletContext() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }

        return sc.getServletContext();
    }
    
    public String getServletInfo() {
    return "";
    }

    public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
    }
   
    public void init() throws ServletException {

    }
         
    public void log(String msg) {
    getServletContext().log(getServletName() + ": "+ msg);
    }
      
    public void log(String message, Throwable t) {
    getServletContext().log(getServletName() + ": " + message, t);
    }
    
    public abstract void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;
    
    public String getServletName() {
        ServletConfig sc = getServletConfig();
        if (sc == null) {
            throw new IllegalStateException(
                lStrings.getString("err.servlet_config_not_initialized"));
        }
        return sc.getServletName();
    }
}

------------------------------------------------------------------------------------->

  实现http协议

  之前已经看到,在service方法中可以转为HttpServletRequest与HttpServletResponse两个接口,从而支持http相关的服务。但每次都做这种强转还是相当麻烦的,所以,我们做如下处理:

HttpServletRequest httpServletRequest = (HttpServletRequest) req;
HttpServletResponse httpServletResponse = (HttpServletResponse) res;

所以,我们需要一个符合http,使用起来相对简便的基础类来作为常用servlet的父类,首先做如下改造:

public class MyHttpServlet extends GenericServlet {
    private static final long serialVersionUID = 1L;

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpServletResponse httpServletResponse = (HttpServletResponse) res;
        service(httpServletRequest,httpServletResponse);
    }

    public void service(HttpServletRequest request, HttpServletResponse response) {
        
    }
}

这样在基类中可以比较方便地使用改造之后的方法,其参数就直接是HttpServletRequest与HttpServletResponse,不用做额外地转换。在service中,可以根据request的访问方法(GET,POST,PUT,DELETE...)对其进行相应的处理,从而衍生出doGet(HttpServletRequest request, HttpServletResponse response),doPost(HttpServletRequest request, HttpServletResponse response)等常用方法。这里可以看看官方给出的HttpServlet源码:

package javax.servlet.http;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;

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
    implements java.io.Serializable
{
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";
    
    private static final String LSTRING_FILE =
    "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings =
    ResourceBundle.getBundle(LSTRING_FILE);

    public HttpServlet() { }
 
    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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
    }


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

    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
    {
    NoBodyResponse response = new NoBodyResponse(resp);
    
    doGet(req, response);
    response.setContentLength();
    }
    

    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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
    }

    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
    {
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_put_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
    }

    protected void doDelete(HttpServletRequest req,
                HttpServletResponse resp)
    throws ServletException, IOException
    {
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_delete_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
    }
    

    private Method[] getAllDeclaredMethods(Class c) {

        if (c.equals(javax.servlet.http.HttpServlet.class)) {
            return null;
        }

        Method[] parentMethods = getAllDeclaredMethods(c.getSuperclass());
        Method[] thisMethods = c.getDeclaredMethods();
    
        if ((parentMethods != null) && (parentMethods.length > 0)) {
            Method[] allMethods =
                new Method[parentMethods.length + thisMethods.length];
        System.arraycopy(parentMethods, 0, allMethods, 0,
                             parentMethods.length);
        System.arraycopy(thisMethods, 0, allMethods, parentMethods.length,
                             thisMethods.length);

        thisMethods = allMethods;
    }

    return thisMethods;
    }
   
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
    {
    Method[] methods = getAllDeclaredMethods(this.getClass());
    
    boolean ALLOW_GET = false;
    boolean ALLOW_HEAD = false;
    boolean ALLOW_POST = false;
    boolean ALLOW_PUT = false;
    boolean ALLOW_DELETE = false;
    boolean ALLOW_TRACE = true;
    boolean ALLOW_OPTIONS = true;
    
    for (int i=0; i<methods.length; i++) {
        Method m = methods[i];
        
        if (m.getName().equals("doGet")) {
        ALLOW_GET = true;
        ALLOW_HEAD = true;
        }
        if (m.getName().equals("doPost")) 
        ALLOW_POST = true;
        if (m.getName().equals("doPut"))
        ALLOW_PUT = true;
        if (m.getName().equals("doDelete"))
        ALLOW_DELETE = true;
        
    }
    
    String allow = null;
    if (ALLOW_GET)
        if (allow==null) allow=METHOD_GET;
    if (ALLOW_HEAD)
        if (allow==null) allow=METHOD_HEAD;
        else allow += ", " + METHOD_HEAD;
    if (ALLOW_POST)
        if (allow==null) allow=METHOD_POST;
        else allow += ", " + METHOD_POST;
    if (ALLOW_PUT)
        if (allow==null) allow=METHOD_PUT;
        else allow += ", " + METHOD_PUT;
    if (ALLOW_DELETE)
        if (allow==null) allow=METHOD_DELETE;
        else allow += ", " + METHOD_DELETE;
    if (ALLOW_TRACE)
        if (allow==null) allow=METHOD_TRACE;
        else allow += ", " + METHOD_TRACE;
    if (ALLOW_OPTIONS)
        if (allow==null) allow=METHOD_OPTIONS;
        else allow += ", " + METHOD_OPTIONS;
    
    resp.setHeader("Allow", allow);
    }
 
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp) 
    throws ServletException, IOException
    {
    
    int responseLength;
    
    String CRLF = "
";
    String responseString = "TRACE "+ req.getRequestURI()+
        " " + req.getProtocol();
    
    Enumeration reqHeaderEnum = req.getHeaderNames();
    
    while( reqHeaderEnum.hasMoreElements() ) {
        String headerName = (String)reqHeaderEnum.nextElement();
        responseString += CRLF + headerName + ": " +
        req.getHeader(headerName); 
    }
    
    responseString += CRLF;
    
    responseLength = responseString.length();
    
    resp.setContentType("message/http");
    resp.setContentLength(responseLength);
    ServletOutputStream out = resp.getOutputStream();
    out.print(responseString);    
    out.close();
    return;
    }        

    protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
    {
    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
        // servlet doesn't support if-modified-since, no reason
        // to go through further expensive logic
        doGet(req, resp);
        } else {
        long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
        if (ifModifiedSince < (lastModified / 1000 * 1000)) {
            // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
            maybeSetLastModified(resp, lastModified);
            doGet(req, resp);
        } else {
            resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);
        
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);    
        
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);
        
    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);
        
    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);
        
    } else {

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
    }

    private void maybeSetLastModified(HttpServletResponse resp,
                      long lastModified) {
    if (resp.containsHeader(HEADER_LASTMOD))
        return;
    if (lastModified >= 0)
        resp.setDateHeader(HEADER_LASTMOD, lastModified);
    }
 
    public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
    {
    HttpServletRequest    request;
    HttpServletResponse    response;
    
    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException("non-HTTP request or response");
    }
    service(request, response);
    }
}

class NoBodyResponse implements HttpServletResponse {
    private HttpServletResponse        resp;
    private NoBodyOutputStream        noBody;
    private PrintWriter            writer;
    private boolean            didSetContentLength;

    // file private
    NoBodyResponse(HttpServletResponse r) {
    resp = r;
    noBody = new NoBodyOutputStream();
    }

    // file private
    void setContentLength() {
    if (!didSetContentLength)
      resp.setContentLength(noBody.getContentLength());
    }

    public void setContentLength(int len) {
    resp.setContentLength(len);
    didSetContentLength = true;
    }

    public void setCharacterEncoding(String charset)
      { resp.setCharacterEncoding(charset); }

    public void setContentType(String type)
      { resp.setContentType(type); }

    public String getContentType()
      { return resp.getContentType(); }

    public ServletOutputStream getOutputStream() throws IOException
      { return noBody; }

    public String getCharacterEncoding()
    { return resp.getCharacterEncoding(); }

    public PrintWriter getWriter() throws UnsupportedEncodingException
    {
    if (writer == null) {
        OutputStreamWriter    w;

        w = new OutputStreamWriter(noBody, getCharacterEncoding());
        writer = new PrintWriter(w);
    }
    return writer;
    }

    public void setBufferSize(int size) throws IllegalStateException
      { resp.setBufferSize(size); }

    public int getBufferSize()
      { return resp.getBufferSize(); }

    public void reset() throws IllegalStateException
      { resp.reset(); }
      
      public void resetBuffer() throws IllegalStateException
      { resp.resetBuffer(); }

    public boolean isCommitted()
      { return resp.isCommitted(); }

    public void flushBuffer() throws IOException
      { resp.flushBuffer(); }

    public void setLocale(Locale loc)
      { resp.setLocale(loc); }

    public Locale getLocale()
      { return resp.getLocale(); }

    public void addCookie(Cookie cookie)
      { resp.addCookie(cookie); }

    public boolean containsHeader(String name)
      { return resp.containsHeader(name); }

    /** @deprecated */
    public void setStatus(int sc, String sm)
      { resp.setStatus(sc, sm); }

    public void setStatus(int sc)
      { resp.setStatus(sc); }

    public void setHeader(String name, String value)
      { resp.setHeader(name, value); }

    public void setIntHeader(String name, int value)
      { resp.setIntHeader(name, value); }

    public void setDateHeader(String name, long date)
      { resp.setDateHeader(name, date); }

    public void sendError(int sc, String msg) throws IOException
      { resp.sendError(sc, msg); }

    public void sendError(int sc) throws IOException
      { resp.sendError(sc); }

    public void sendRedirect(String location) throws IOException
      { resp.sendRedirect(location); }
    
    public String encodeURL(String url) 
      { return resp.encodeURL(url); }

    public String encodeRedirectURL(String url)
      { return resp.encodeRedirectURL(url); }
      
    public void addHeader(String name, String value)
      { resp.addHeader(name, value); }
      
    public void addDateHeader(String name, long value)
      { resp.addDateHeader(name, value); }
      
    public void addIntHeader(String name, int value)
      { resp.addIntHeader(name, value); }
     
    public String encodeUrl(String url) 
      { return this.encodeURL(url); }

    public String encodeRedirectUrl(String url)
      { return this.encodeRedirectURL(url); }

}

class NoBodyOutputStream extends ServletOutputStream {

    private static final String LSTRING_FILE =
    "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings =
    ResourceBundle.getBundle(LSTRING_FILE);

    private int        contentLength = 0;

    // file private
    NoBodyOutputStream() {}

    // file private
    int getContentLength() {
    return contentLength;
    }

    public void write(int b) {
    contentLength++;
    }

    public void write(byte buf[], int offset, int len)
    throws IOException
    {
    if (len >= 0) {
        contentLength += len;
    } else {
        // XXX
        // isn't this really an IllegalArgumentException?
        
        String msg = lStrings.getString("err.io.negativelength");
        throw new IOException("negative length");
    }
    }
}

------------------------------------------------------------------------------------->

  最后再说说ServletContext的作用:

  它是一个接口javax.servlet.ServletContext,tomcat等web容器在启动时会创建一个实例,服务器关闭时销毁。主要作用是在一个web项目中共享数据、管理web项目资源,为整个web配置公共信息等。

  api:

web项目中共享数据:提供属性操作  --xxxAttribute
        setAttribute(String name,Object obj ) 给web项目公共区域存放内容。(任意内容)
        getAttribute(String name) 通过指定名称获得内容
        removeAttribute(String name) 通过指定名称移除内容。
        如果不想使用数据,要么移除,要么关闭服务器。
整个web项目初始化参数
        getInitParameter(java.lang.String name) 获得指定名称,初始化参数的值
        getInitParameterNames() 获得所有的初始化参数的名称
        web.xml 配置 整个web项目的初始化
        <context-param><param-name><param-value>
        spring 在web项目中配置<context-param><param-name>contextConfigLocation<param-value>classpath:applicationContext.xml
获得web项目资源
        String getRealPath(java.lang.String path) 获得发布到tomcat下的指定资源路径
        例如:getRealPath("/WEB-INF/web.xml")
        获得实际路径:D:java omcatapache-tomcat-7.0.53webappsTestServletWEB-INFweb.xml
        InputStream getResourceAsStream(java.lang.String path)  与getRealPath使用相同,返回不同
        getResourcePaths(java.lang.String path)  指定路径下的所有内容。
        例如:getResourcePaths("/WEB-INF/")

原文地址:https://www.cnblogs.com/bruceChan0018/p/5868504.html