Servlet详解

一、简介

Servlet是server+Applet的缩写,表示一个服务器应用。Servlet就是一套规范,按照这套规范写的代码就可以直接在Java服务器上面运行。

二、Servlet接口

Servlet是一套规范,那么在Java中规范则是接口。

2.1 Servlet3.1中Servlet的接口定义如下

public interface Servlet {
    public void init(ServletConfig config) throws ServletException;
    public ServletConfig getServletConfig();    
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;	
    public String getServletInfo();    
    public void destroy();
}

Servlet接口介绍

web.xml配置

    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.lbx.servlet.HelloServlet</servlet-class>
        <init-param>
            <param-name>initParam</param-name>
            <param-value>initValue</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

1 public void init(ServletConfig config) throws ServletException;

 <load-on-startup>值</load-on-startup> 

  1. init()方法初始化内容,被调用时,web容器会把config依赖传进去。
  2. load-on-startup的值不为负数时,web容器启动时会调用init方法。
  3. 没有填写load-on-startup标签或者标签里面的值是负数的话,访问该Servlet的时候会才会调用init方法。
  4. 同一个Servlet的init方法在整个流程中只会调用一次。

2  public  ServletConfig getServletConfig();  

        getServletConfig()方法用来获取Servlet的配置,比如上面 <init-param>标签里面的参数。下面会详细介绍这个方法。

3 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; 

        service方法用于处于请求,web容器会解析HTTP协议,封装成对象传进去。

4  public String getServletInfo();  

        getServletInfo()获取servlet相关的信息,如作者、版权等,这个方法在需要自己实现,默认返回空字符串。

5 public void destroy();

        destory()方法主要用于Servlet的销毁,当应用从tomcta移除或者关闭服务器时会被调用,用于释放资源,只会调用一次。

2.2 ServletConfig接口定义

public interface ServletConfig {
    public String getServletName();
    public ServletContext getServletContext();
    public String getInitParameter(String name);
    public Enumeration<String> getInitParameterNames();
}

ServeltConfig接口介绍

  1. getServeltName():用于获取Servlet的名字,也就是web.xml中定义的servlet-name。
  2. getServeltContext():获取ServeltContext对象,全局共享这个对象,一个应用中只能有一个ServletContext对象。可获取<context-param>里面内容。
  3. getInitParameter(): 获取init-param配置的值。
  4. getInitParameterNames():获取配置的所有Init-param的名字集合。

三、GenericServlet

     GenericServlet这个类实现了Servlet和ServletConfig接口(service()方法用abstract修饰了),可以直接调用Servlet和ServletConfig里的方法,比如获取ServletConfig中的方法时候可以直接调用,而无须调用getServletConfig().getServletContext()了,不过底层实现其实是在内部调用了,代码如下:

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

        return sc.getServletContext();
    }

GenericServlet实现了Servlet的init(ServletConfig config)方法,在里面将config复制给了内部变量config,然后调用无参的init()方法,这个方法是模板方法,在子类中可以通过覆盖它来完成自己的初始化工作。

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

}

这种做法有三个作用:

  1. 将参数config设置给内部属性config,这样有其他地方需要这个对象就可直接调用。
  2. 做初始化操作时,不用关心config对象。
  3. 重写init()方法不需要调用super.init(config)。

四、HttpServlet

这个类是我们最常使用的类,继承了GenericServlet,写servlet直接继承就可以了,无需重新实现Servlet接口,这个类主要作用是如何处理请求。

看代码:

    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }
        //向下转型为HttpServletRequest
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
        //调用http的处理方法
        service(request, response);
    }

问题一:为什么可以将ServletRequest 强转为HttpServletRequest呢?

     因为在tomcat内部创建的这个request它就是httpServletRequest接口的子类。

问题二:为什么要将ServletRequest 强转为HttpServletRequest呢?

  1. 因为ServletRequest中只提供了获取基本信息的方法。没有获取用户请求类型的方法,而且还包含了许多方法。

执行完上面方法,最后会调用service方法,调用的不是用一个service,因为传入参数类型不一样,重载方法

这个方法的作用是:获取Http请求类型,将不同请求类型路由到不同的处理方法。具体方法都是doXXX的结构,doGet,doPost,doPut,doDelete方法都是模板方法,而且如果子类没有实现将返回404错误页面。

 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) {
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                   
                    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);
        }
    }

流程图

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券