zoukankan      html  css  js  c++  java
  • 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-Modifiedif-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);
    
  • 相关阅读:
    echarts事件触发多次解决办法
    nginx代理出现的问题
    iframe页面之间通信
    获取当前日期的前XX天或者后XX天
    获取本月往后一年的日期月份信息
    Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)D. Take a Guess
    Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)C. Compressed Bracket Sequence
    Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)B. Take Your Places!
    Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2) A. A Variety of Operations
    centos 查看mysql数据库命令
  • 原文地址:https://www.cnblogs.com/maying3010/p/8447719.html
Copyright © 2011-2022 走看看