Servlet详解

本文详细介绍了Servlet的概念及其实现机制,包括Servlet接口的五个方法及其用途,ServletConfig和ServletContext的作用与使用方式,以及GenericServlet和HttpServlet的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Servlet是Server+Applet的缩写,表示一个服务器的应用。

1、Servlet接口

 Servlet是一套规范,即就是一个接口,包含五个方法:

        <1>Init方法:在容器启动时被容器调用(当在web.xmlload-on-startup设置为负数或者不设置时会在Servlet第一次调用时才被调用)只用调用一次

        <2>getServletConfig方法用于获取ServletConfig

        <3>service方法用于具体处理一个请求

        <4>getServletInfo方法可以获取一些Servlet相关的信息(这个方法需要自己实现,默认返回空字符串)

        <5>destory是在Servlet销毁时释放一些资源(只会调用一次)

Init方法被调用时会接受到一个ServletConfig类型的参数,是容器传进来的。Web.xml中定义Servlet时通过init-param标签配置的参数就是通过ServletConfig来保存的,比如:定义Spring MVCServlet时指定配置文件的contextConfigLocation参数就保存在ServletConfig

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

Tomcat中对的Servletinit方法是在org.apache.catalina.core.StandardWrapperinitServlet方法中调用的,ServletConfig传入的是StandardWrapper(里面封装着Servlet)吱声的门面类StandardWrapperFacadeServlet是通过xml文件配置的,在解析xml时就会把配置参数的设置进去,这样StandardWrapper本身就包含了配置项了,当然,不是StandardWrapper的所有内容都是Config相关的,所以使用了Facade类。

ServletConfig接口的定义:

package javax.servlet;
import java.util.Enumeration;
public interface ServletConfig {
    public String getServletName();
    public ServletContext getServletContext();
    public String getInitParameter(String name);
    public Enumeration<String> getInitParameterNames();
}

        getServletName用于获取Servlet的名字,即在web.xml中定义的servlet-name

        getInitParameter方法用户获取init-param配置的参数

        getInitParameterNames用于获取配置的所有init-param的名字集合

        getServletContext返回值ServletContext代表的是我们这个应用本身,ServletContext里面设置的参数是可以被当前应用的所有Servlet共享了。

ServletConfigServlet级的,ServletContextContext(也就是Application)级的。

除了以上两个级别的可以操作,还有更高级别的。在ServletContext接口中有一个public ServletContext getContext(String uripath),可以根据路径获取到同一个站点下别的应用的ServletContext

ServletConfigServletContext最常见的使用方式就是传递初始化参数,

<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring/spring-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
  </context-param>

通过context-param配置log4jConfigLocationServletContext中,通过servlet下的init-param配置的contextConfigLocation配置到ServletConfig

 获取方法如下:

String contextConfigLocation = getServletConfig().getInitParameter("contextConfigLocation");
String log4jConfigLocation = getServletConfig().getServletContext().getInitParameter("log4jConfigLocation");

为了方便操作,GenericServlet定义了getInitParameter方法,内部返回getServletConfig().getInitParameter的返回值,因此,需要获取ServletConfig中的参数,可以不再调用getServletConfig(),而是直接调用getInitParameter

ServletContext中还非常常用的就是保存Application级的属性,如:getServletContext().setAttribute("log4jConfigLocation", "/WEB-INF/spring/log4j.properties");

注意:设置同名Attribute并不会覆盖initParameter中的参数,ServletConfig不可以设置属性。

2、GenericServlet

GenericServletServlet的默认实现,主要做三件事:

(1)实现了ServletConfig接口:我们不需要再获取ServletConfig,直接调用ServletConfig中方法

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

        return sc.getServletContext();
    }

(2)提供了无参的init方法:实现了Servletinit(ServletConfig config)方法,在方法中将config设置给了内部变量config,然后调用了无参的init()方法

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

以上两种做法的作用有三点:

ServletConfig的接口方法中直接调用config的相应方法来执行

我们自己在写Servlet时只处理自己的初始化逻辑,不需要再关心config

重写无参init方法无需再调用super.init(config),重写有参的init方法,一定要记得调用super.init(config),否则这里的config属性就接受不到值,相应的ServletConfig接口方法也就不能执行了。

(3)提供了log两个方法:一个记录日志,一个记录异常,具体实现是通过传给ServletContext的日志实现的。

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

3、HttpServlet

HttpServletHTTP协议实现的Servlet的基类,我们写Servlet时直接继承它就可以了。

HttpServlet是跟协议有关的,所以HttpServlet主要是重写了service方法。在service方法中将ServletRequestServletResponse转换为HttpServletRequestHttpServletResponse,然后根据Http请求的类型不同将请求路由到把不同的处理方法。

代码如下:

    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) {
                // 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) {
                    // 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 {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            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);
        }
    }

备注:

源代码来源于javax.servlet-api-3.0.1.jar中。

参考资料:《看透Spring MVC 源代码分析与实践》



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值