Servlet详解

Servlet详解


       servlet是Server+Applet的缩写,表示服务器应用。Servlet其实就是一套规范,按照servlet规范编写的程序可以直接运行在支持servlet规范的java服务器上。Servlet3.1的Servlet规范的结构图如下:

Servlet接口


       Servlet是一套规范,那么里面的Servlet结构自然很重要了。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();
}


       init在容器启动时会被容器调用(load-on-startup设置为负数或者不设置时会在Servlet第一次被调用时才被调用),只会调用一次;getServletConfig用户获取ServletConfig;service方法用于处理一个具体的请求;getServletInfo方法用于获取Servlet相关信息,这个方法要自己实现,默认返回空字符串。destory主要用于Servlet销毁时释放资源,一般是关闭服务器时,也只会调用一次。

       init方法被调用时会接收到一个ServletConfig参数,通过容器传入。ServletConfig是指Servlet的配置,我们在web.xml中定义Servlet时通过init-param标签配置的参数就是通过ServletConfig保存的,我们配置SpringMvc的Dispatcher时指定的ContextConfiguration就是保存在ServletConfig中。

    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

tomcat中的Servlet的init方法是在org.apache.catalina.core.SrandardWrapperd的initServlet方法中调用的。ServletConfig传入的是StandardWrapper(封装着Servlet)的门面类ServletWrapperFacade。Servlet是解析xml时配置的,这样StandardWrapper就包含了配置项。

       ServletConfig接口定义如下:

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用来获取配置参数的集合。getServletContext类的返回值代表我们这个应用的本身,ServletContext是Tomcat的context门面类ApplicationContextFacade。SerlvetContext代表应用本身,那么在ServletContext里面的参数就可以被所有的Servlet共享了。我们项目设置参数有些保存在session中,有时候保存在Application中,后者大多就是保存在ServletContext中。

       servletConfig是Servlet级的配置,ServletContext是应用级的,两者都可以配置参数,但是ServletContext还有更多功能。我们知道tomcat是可以同时运行多个应用的,那么有没有更高级别的操作可以访问到其他的应用呢?ServletContext接口中有个方法ServletContext getContext(String uripath),可以根据路径获取到同一站点下别的应用的ServletContext。当然为了安全这个方法默认返回null,如果要使用需要进行一些配置。

       ServletConfig和ServletContext最常见的使用时传递参数。以SpringMvc的配置为例:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring-*.xml</param-value>
    </context-param>
    <servlet>
        <servlet-name>springMvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

通过context-param配置的参数在ServletContext中,通过Servlet的init-param配置的contextConfigLocation配置到了ServletConfig中。可以在Servlet中通过getInitparameter获取:

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

String servletConfigLocation = getServletConfig().getInitParameter("contextConfigLocation");


       在GenericServlet中也有getInitParameter方法,内部返回返回getServletConfig().getInitParameter的返回值,因为我们可以直接调用getInitParameter获取ServletConfig的参数。

       ServletContext经常用来保存Application级的属性,可以使用setAttribute来进行:

getServletContext().setAttribute(String keyString value);

ServletConfig和ServletContext是两套数据,相互间不干扰,servletConfig不能设置属性。

GenericServlet


       GenericServlet是Servlet的默认实现,主要有三个功能:
- 实现ServletConfig接口
- 提供无参的init方法
- 提供log方法


       GenericServlet提供了getServletContext接口,内部调用getServletConfig方法,不用我们再去getServletConfig().getServletContext()接口了。

       GenericServlet实现了Servlet的init接口,将config赋值给了内部的config变量,然后调用无参的init方法,我们可以通过继承他来覆盖init方法。

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

这样我们就可以直接在ServletConfig方法中直接调用config来操作,而且在写Servlet时就不用关心config,只用处理自己的初始化逻辑即可,即覆盖无参的init方法即可。如果要覆盖带参数的init方法,需要调用super.init(config)。

       GenericServlet提供了两个log方法。一个记录日志,一个记录异常,通过传递给ServletContext实现的。

    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的基类,写Servlet时直接继承即可。SpringMvc中的Dispatcher即是继承的HttpServlet。httpServlet是与协议相关的,其主要关注点在于如何处理请求。所以HttpServlet主要重写了Servlet接口中的service方法。在service方法中,将ServletRequest和ServletResponse转化为了HttpServletRequest和httpServletResponse,再根据不同的请求类型将请求路由到不同的方法。

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

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


       具体处理方法都是doXXX的形式,如doGet(),doPost()等常用方法。doGet,doPost,doPut,doDelete这些都是模板方法,如果子类没有实现将会出现异常,在调用doGet前会对是否过期做检查,如果没过期直接返回304状态码使用缓存。doHead调用doGet返回空body的response。doOptions主要用来进行调试,返回支持处理类型的集合。doTrace用于远程诊断服务器,将收到的header原样返回,这种做法容易被黑客利用,最好禁用。doOptions和doTrace功能固定,所以httpServlet提供了默认实现。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值