Servlet3.1学习

Posted 默默的看雨下

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet3.1学习相关的知识,希望对你有一定的参考价值。

Servlet

Servlet是基于Java技术的Web组件,被容器管理,用于生成动态内容。可以被基于Java技术的Web Server动态加载并运行。客户端通过Servlet容器实现的请求/应答模型与Servlet交互。

Servlet容器

Servlet容器是Java Web Server的一部分,它提供接受请求和发送响应的服务,基于MIME编解码请求和响应,Servlet容器同样能够管理Servlet生命周期。

Servlet接口

Servlet生命周期: Servlet生命周期由容器管理。在Servlet规范中规定了Servlet如何被加载、实例化、初始化、处理请求、结束服务。

  • 加载、实例化: 加载就是Servlet类的加载,我们可以通过文件、网络流等加载Servlet,容器通过自定义的类加载器可以实现多个Servlet项目在容器中运行,互不影响。在Servlet中加载和实例化是一起的,我们可以配置Servlet使得加载和实例化可以发生在容器启动时,或者发生在该Servlet第一次处理请求时。通过Servlet属性==loadOnStartup==来配置,当loadStartup大于0时会在容器启动时就加载和实例化,默认值为-1(第一次处理请求时加载和实例化)。
  • 初始化: Servlet的处理化通过调用init(ServletConfig)方法进行初始化。SerlvetConfig对象可以访问配置的Servlet InitParam属性,然后我们可以在初始化方法中进行其它的初始化操作。例如在Spring MVC中我们通过配置Dispatcher Servlet Param属性把Spring相关信息注入,然后在init()方法中获取并进行Spring容器的初始化,代码如下:

    <!-- 配置DispatcherServlet -->
    <servlet>
    <servlet-name>seckill-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 配置springMVC需要加载的配置文件 
        spring-dao.xml spring-service.xml spring-web.xml-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/spring-*.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>seckill-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>

    注意:一个Servlet对应一个ServletConfig,所以我们不应该把全局信息放入ServletConfig,除非只有一个Servlet。

  • 处理请求: Servlet通过调用serivce(request,response)方法处理请求。客户端请求信息有ServletRequest及其子类表示,服务器响应信息有ServletResponse及其子类表示,Servlet通过调用service()方法处理请求并回复响应。由于容器可以并发的调用Servlet的service()方法,所以我们在实现service()方法或其子方法,例如doGet()方法时需要注意线程安全问题。
  • 结束服务: 容器调用Servlet的destory()方法进行结束服务,然后释放该Servlet实例以供GC。我们可以在destory()方法中释放其使用的资源或者保持其持久化状态(HttpServlet实现了Serializable接口)。

实例化数量: 一般来讲,Servlet容器会对每个Servelt必须实例化一个对象(有且只有一个,实现了SingleThreadModel接口除外)。在分布式中,容器需要对每个JVM中个每个Servlet实例化一个对象(实现实现了SingleThreadModel接口除外)

处理请求: Servlet通过service()方法处理请求。通常我们继承HttpServlet,其实现的service()方法会自动把Http请求依照协议转发到相关方法上。

  • doGet 处理 Http Get请求
  • doPost 处理 Http Post请求
  • doOptions 处理 Http Options请求
  • ....

代码如下:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            // GET请求会有特殊的处理
        } 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);
        }
    }

特别的GET处理(有条件支持): 在Http缓存规范中(可参考这篇文章)有==Last-Modified==和==if-Modified-Since==两个Header来进行Http缓存,Servlet处理GET请求时会通过处理这两个字段,有条件的支持缓存(依赖于Servlet的实现),代码如下:

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 {
        // 省略其它处理逻辑
    }
}

编写Servlet只需要重写getLastModified()方法即可,如下代码:

@Override
protected long getLastModified(HttpServletRequest req) {
    return LocalDateTime.of(2018, 2, 6, 14, 9, 50)
    .toInstant(ZoneOffset.of("+8"))
    .toEpochMilli();
}

第一次访问Response Header如下:

HTTP/1.1 200
Last-Modified: Tue, 06 Feb 2018 06:09:50 GMT
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 06 Feb 2018 07:06:22 GMT

后面访问Request Header及Response Header如下:

GET /servlet/cacheServlet HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
If-Modified-Since: Tue, 06 Feb 2018 06:09:50 GMT

HTTP/1.1 304
Date: Tue, 06 Feb 2018 07:07:29 GMT

异常处理: Servlet在初始化(调用init()方法)和处理请求(调用service()方法)时,会抛出异常。

  • 初始化时异常: 抛出ServletException异常时会停止初始化,且以后也不会初始化;抛出UnavailableExceptioin异常时,容器会在不可用时间过后继续初始化Serlvet。也就是说当请求会被容器分发到其他Servlet中去处理,相当于没有该Servlet。
  • 处理请求时异常: 与初始化异常一样都可能抛出两种异常,且异常意义一样。但容器会继续都让该Servlet处理请求,抛出ServletException异常时会返回Http Response 500异常;抛出UnavailableException异常时会返回Http Response 503异常。

ServletContext

ServletContext(Servlet上下文)代表Servlet运行在Web应用的视图,它代表整个当前整个Web应用。ServletContext可以动态添加Servlet、Filter、Listener,可以获取当前Web应用下的所有资源,存放Servlet Attribute、Parameter属性以供所有Servlet访问。

每个部署到容器的Web应用都有一个与之对于的ServletContext,在分布式环境下每个JVM中的每个Web应用都有其对于的ServletContext。ServletContext是线程不安全的,所以所有的Servlet Parameter、Attribute都应该是线程安全的。

动态加载Servlet、Filter、Listener

Servlet3.0以后ServletContext支持动态添加Servlet、Filter、Listener,可以编程式的开发Web应用。其只能在实现ServletContextListener的contexInitialized方法中动态的添加,否则会抛异常,下面是其动态添加方法的API的注释:

@throws IllegalStateException if this ServletContext has already been initialized

@throws IllegalArgumentException if <code>filterName</code> is null or an empty String

@throws UnsupportedOperationException if this ServletContext was 
passed to the {@link ServletContextListener#contextInitialized} method
of a {@link ServletContextListener} that was neither declared in
<code>web.xml</code> or <code>web-fragment.xml</code>, nor annotated
with {@link javax.servlet.annotation.WebListener}

具体接口实例如下,其中返回Registartion对象可以配置UrlMapping等属性:

public ServletRegistration.Dynamic addServlet(String servletName, Class <? extends Servlet> servletClass);

public FilterRegistration.Dynamic addFilter(String filterName, Class <? extends Filter> filterClass);

public void addListener(Class <? extends EventListener> listenerClass);

Spring中的ServletRegistrationBean、FilterRegistrationBean等就是通过ServletContext编程式的动态添加Servlet、Filter

获取资源
ServletContext接口提供访问Web应用资源的方法。Web应用资源指的是该Web应用下的所有资源,包括WEB-INF、自定义静态资源文件等等。ServletContext获取资源API如下所示,其中path必须以"/"开头,表示该资源是相对于该Web应用下的资源:

/**
 * Returns a URL to the resource that is mapped to the given path.
 * <p>The path must begin with a <tt>/</tt> and is interpreted
 * as relative to the current context root,
 * or relative to the <tt>/META-INF/resources</tt> directory
 * of a JAR file inside the web application‘s <tt>/WEB-INF/lib</tt>
 * directory.
 */
public URL getResource(String path) throws MalformedURLException;

/**
 * path参数与上面方法一样,只是返回的是Inputstream
 */
public InputStream getResourceAsStream(String path);

/**
 * path参数与上面方法一样,只是返回的是当前资源下的子资源
 */
public Set<String> getResourcePaths(String path);

/**
 * path参数与上面方法一样,只是返回的是当前主机下该资源的真实目录
 */
public String getRealPath(String path);

以上是关于Servlet3.1学习的主要内容,如果未能解决你的问题,请参考以下文章

java SpringRetry学习的代码片段

python 机器学习有用的代码片段

学习笔记:python3,代码片段(2017)

学习 PyQt5。在我的代码片段中找不到错误 [关闭]

PHP必用代码片段

Servlet3.1上传图片示例