Servlet

Servlet

定义
Servlet 是 Serve + Applet 的缩写,表示一个服务器应用。Servlet 规范结构如图所示:
Servlet结构

Servlet 接口

Servlet 接口定义了5个方法:
Servlet接口方法

  • void init(ServletConfig config)
    在容器启动时被调用(当 load-on-startup 设置为负数或者不设置时会在 Servlet 第一次用到时才被调用),只会调用一次。

  • ServletConfig getServletConfig()
    获取 ServletConfig。

  • void service(ServletRequest req, ServletResponse res)
    具体处理一个请求。

  • String getServletInfo()
    获取与 Servlet 相关的信息,需要自己实现,默认返回空字符串。

  • void destroy()
    在 Servlet 销毁时释放资源,只会调用一次。

ServletConfig 接口

ServletConfig 接口定义了4个方法:
ServletConfig接口方法

  • String getServletName()
    获取 Servlet 的名字,即在 web.xml 中定义的 <servlet-name>

  • ServletContext getServletContext()
    获取 ServletContext。

  • String getInitParameter(String name)
    获取 <init-param> 配置下的 <param-value> 参数集合。

  • Enumeration<String> getInitParameterNames()
    获取 <init-param> 配置下的 <param-name> 参数集合。

ServletContext 应用
容器启动时,自动为 Web 应用程序创建 ServletContext 对象,它代表当前 Web 应用,由所有 Servlet 共享,因此 Servlet 对象之间可以通过 ServletContext 对象来实现通讯。

   // Servlet1:将数据 data 存储到 ServletContext 对象中
   String data = "Save ServletContext !";
   ServletContext context = this.getServletContext();
   context.setAttribute("data", data);

   // Servlet2:从 ServletContext 对象中取出数据,实现数据共享
   ServletContext context = this.getServletContext();
   String data = (String) context.getAttribute("data");
   response.getWriter().print("data="+data);

GenericServlet

GenericServlet 是 Servlet 的默认实现,主要做了三件事:①实现 ServletConfig 接口;②提供了无参的 init 方法;③提供了 log 方法。

1. 实现 ServletConfig 接口

通过源码可知,getServletContext 方法内部先调用了 getServletConfig,因此可以直接调用 ServletConfig 中的方法。

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

        return sc.getServletContext();
    }

2. 提供了无参的 init 方法

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

    // javax.servlet.GenericServlet
    public void init(ServletConfig config) throws ServletException {
	this.config = config;
	this.init();
    }
    
    public void init() throws ServletException {

    }

三个作用

  1. 将参数 config 设置给内部属性 config,方便在 ServletConfig 的接口方法中直接调用 config 的方法来执行
  2. 自定义实现初始化逻辑,不需要关心 config
  3. 重写 init 方法时不需要调用 super.init(config)。如果自定义的 Servlet 中重写了带参数的 init 方法,那么一定要调用 super.init(config),否则 config 属性接收不到值,相应的 ServletConfig 接口方法便无法执行。

3. 提供了 log 方法

2个 log 方法,一个记录日志,一个记录异常。具体实现是通过传给 ServletContext 的日志框架实现的。

    // javax.servlet.GenericServlet
    public void log(String msg) {
	getServletContext().log(getServletName() + ": "+ msg);
    }
   
    public void log(String message, Throwable t) {
	getServletContext().log(getServletName() + ": " + message, t);
    }

HttpServlet

HttpServlet 是 HTTP 协议实现的 Servlet 的基类,继承 GenericServlet,因此包含了 GenericServlet 相关的特性。

HttpServlet 主要重写了 Servlet 接口的 service 方法,将 ServletRequest 和 ServletResponse 转换为 HttpServletRequest 和 HttpServletResponse ,然后根据 HTTP 请求的类型不同将请求路由到不同的处理方法。具体处理方法 doXXX 都是模板方法,需要子类覆写实现,否则抛出异常。

    // javax.servlet.HttpServlet
    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");
        }
        
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }
    
    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);
        }
    }

使用

Servlet 配置

<servlet> 用于注册 Servlet ,它包含有两个主要的子元素: <servlet-name><servlet-class>,分别用于设置 Servlet 的注册名称和完整类名。

<servlet-mapping> 用于映射一个已注册的 Servlet 的一个对外访问路径,它包含有两个子元素:<servlet-name><url-pattern>,分别用于指定 Servlet 的注册名称和对外访问路径。

映射 URL 中可以使用*通配符,但是只能有两种固定的格式:一是"*.扩展名",另一种是以"/*"。如果 Servlet 的 URL 映射路径仅仅为一个"/",那么这个 Servlet 就成为当前 Web 应用程序的缺省 Servlet 。凡是在 web.xml 文件中找不到匹配的 <servlet-mapping> 的 URL ,它们的访问请求都将交给缺省 Servlet 处理。

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.chen.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

应用

web.xml 配置

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:application-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>WEB-INF/dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

通过 <context-param> 指定 Spring 的配置文件位置 contextConfigLocation 参数,并保存在 ServletContext 中;通过 <init-param> 指定 Spring MVC 的配置文件位置 contextConfigLocation 参数,并保存在 ServletConfig 中,其中 <load-on-startup> 设置为1(数字越小优先级别越高),表示该 Servlet 在容器一启动时就立即加载,并调用接收了 ServletConfig 参数的 init 方法。

参考

  1. 《看透Spring MVC源代码分析与实践》
  2. Servlet开发–孤傲苍狼
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值