您的位置:首页 > Web前端 > JavaScript

Servlet&JSP 第五章 进阶API、过滤器与监听器

2017-06-07 12:11 344 查看

一、Servlet进阶API

    每个Servlet都必须由Web容器读取Servlet设置信息、初始化等,才可以成为一个真正的Servlet。对于每个Servlet的设置信息,Web容器会为其生成一个Servlet
Config作为代表对象,可以该对象取得Servlet初始化参数,以及代表整个Web应用程序的Servlet
Config对象。

1、Servlet、Servlet Config与GenericServlet

(1)在Servlet接口上,定义了与Servlet生命周期及请求服务相关的init()、service()与destroy()方法,每一次请求来到容器时,会产生HttpServletRequest与HttpServletResponse对象,并在调用service()方法时当作参数传入。在Web容器启动后,会读取Servlet设置信息,将Servlet类加载并实例化,并为每个Servlet设置信息产生一个ServletConfig对象,而后调用Servlet接口的init()方法,并将产生的ServletConfig对象当作参数传入,这个过程只会在创建Servlet实例后发生一次,之后每次请求到来,调用Servlet实例的service()方法进行服务。

(2)ServletConfig即每个Servlet设置的代表对象,容器会为每个Servlet设置信息产生一个Servlet以及ServletConfig实例。

(3)GenericServlet同时实现了Servlet及ServletConfig。GenericServlet主要的目的就是将初始Servlet调用init()方法传入的ServletConfig封装起来:

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


    GenericServlet在实现Servlet的init()方法时,也调用了另一个无参数的init()方法,在编写Servlet时,如果有一些初始时所要运行的动作,可以重新定义这个无参数的init()方法,而不是直接重新定义有ServletConfig参数的init()方法。

    GenericServlet也包括了Servlet与ServletConfig所定义方法的简单实现,实现内容主要是通过ServletConfig来取得一些相关信息。

2、使用ServletConfig

(1)ServletConfig相当于个别Servlet的设置信息的代表对象,这意味着可以从ServletConfig中取得Servlet设置信息,ServletConfig定义了getInitParameter()、getInitParameterNames()方法,可以取得设置Servlet时的初始参数。

(2)若要使用标注设置个别Servlet的初始参数,可以在@WebServlet中使用@WebInitParam设置initParams属性。若要在web.xml中设置个别Servlet的初始参数,可以在<servlet>标签中使用<init-param>等标签进行设置,web.xml中额设置会覆盖标注的设置。若要用web.xml覆盖标注设置,web.xml中的<servlet-name>设置必须与@WebServlet的name属性相同。

(3)由于ServletConfig必须在Web容器将Servlet
12cdb
实例化后,调用有参数的init()方法再将之传入,是与Web应用程序资源相关的对象,所有在继承HttpServlet之后,通常会重新定义无参数的init()方法以进行Servlet初始参数的取得。

3、使用ServletContext

(1)ServletContext接口定义了运行Servlet的应用程序环境的一些行为与观点。可以使用ServletContext实现对象来取得所请求资源的URL、设置与存储属性、应用程序初始参数,设置动态设置Servlet实例。当整个Web应用程序加载Web容器后,容器会生成一个ServletContext对象作为整个应用程序的代表,并设置给ServletConfig,只要通过ServletConfig的getServletContext()方法就可以取得ServletContext对象。

(2)getRequestDispatcher()方法:用来取得RequestDispatcher实例,使用时路径的指定必须是以“/”作为开头,这个斜杠代表应用程序环境根目录(Context
Root),取得RequestDispetcher实例后,就可以进行请求的转发或包含。

context.getRequestDispatcher("/pages/come.jsp").forward(request,response);


以“/”作为开头有时称为环境相对路径,没有以“/”作为开头则称为请求相对路径,实际上HttpServletRequest的getRequestDispatcher()方法在实现时,若是环境相对路径,则直接委托给ServletContext的getRequestDispatcher();若是请求相对路径,则转换为环境相对路径,再委托给ServletContext的getRequestDispatcher()来取得RequestDispetcher。

(3)getResourcePaths()方法:知道Web应用程序的某个目录中有哪些文件,这个方法会连同WEB-INF的信息都列出来,如果是个目录信息,则会以“/”作结尾。

例1、for(String avatar:getServletContext().getResourcePaths("/")){
//显示avatar文字...
}


(4)getResourceAsStream()方法:在Web应用程序中读取某个文件的内容,使用时指定路径必须以“/”作为开头,表示相对于应用程序环境根目录,或者相对/WEB-INF/lib中JAR文件里META-INF/resources的路径,运行结果会返回InputStream实例,接着就可以运用它来读取文件内容。

    每个Web应用程序都会有一个相对应的ServletContext,针对“应用程序”初始化时需要用到的一些参数数据,可以在web.xml中设置应用程序初始参数,通常这会结合ServletContextListener来做。

二、应用程序事件、监听器

    Web容器管理Servlet/JSP相关的对象的生命周期,若对HttpServletRequest对象、ServletSession对象、ServletContext对象在生成、销毁或相关属性设置发生的时机点有兴趣,则可以实现对应的监听器(Listener),做好相关的设置。

1、ServletContext事件、监听器

    与ServletContext相关的监听器有ServletContextListener与ServletContextAttributeListener。

(1)ServletContextListener:“生命周期监听器”,如果想要知道何时Web应用程序已经初始化或即将结束销毁,可以实现ServletContextListener:

@WebListener     //使用@WebListener标注,容器在部署应用程序时,会实例化并注册给应用程序
public class ContextParamterReader implements ServletContextListener{     //实现ServletContextListener
public void contextInitialized(ServletContextEvent sce){
ServletContext context=sce.getServletContext();     //取得ServletContext
String avatars=context.getInitParameter("AVATAR");     //取得初始参数
context.setAttribute("avatars",avatars);     //设置ServletContext属性
}
public void contextDestroyed(ServletContextEvent sce){}
}


    ServletContextListener可以直接使用@WebListener标注,而且必须实现ServletContextListener接口,这样容器会在启动时加载并运行对应的方法,当Web容器调用了contextInitialized()或contextDestroyed()方法时,会传入ServletContextEvent,其封装了ServletContext,可以通过ServletContextEvent的getServletContext()方法取得ServletContext,通过ServletContext的getInitParameter()方法来读取初始参数,因此Web应用程序初始参数常被称为ServletContext初始参数。

    在整个Web应用程序生命周期,Servlet需共享的资料可以设置为ServletContext属性,由于ServletContext在Web应用程序存活期间都会一直存在,所以设置为ServletContext属性的数据,除非主动移除,否则也是移植存活与Web应用程序中。可以通过ServletContext的setAttribute()方法设置对象为ServletContext属性,之后通过ServletContext的getAttribute()方法取出该属性,若要移除属性,则通过ServletContext的removeAttribute()方法。

(2)ServletContextAttributeListener:“监听属性改变的监听器”,如果想要对象被设置、移除或替换ServletContext属性,可以收到通知以进行一些操作,则可以实现ServletContextAttributeListener。

    当在ServletContext中添加属性、移除属性或替换属性时,相对应的attributeAdded()、attributeRemoved()与attributereplaced()方法就会被调用。

2、HttpSession事件、监听器

    与HttpSession相关的监听器有四个:HttpSessionListener、HttpSessionAttributeListener、HttpSessionBindingListener与HttpSessionActivationListener。

(1)HttpSessionListener:“生命周期监听器”,如果需要在HttpSession对象创建或结束时,做些相对应动作,则可以实现HttpSessionListener:

public interface HttpSessionListener extends EventListener{
public void sessionCreated(HttpSessionEvent se);
public void sessionDestroyed(HttpSessionEvent se);
}


    在HttpSession对象初始化或结束前,会分别调用sessionCreated()与sessionDestroyed()方法,可以通过传入HttpSessionEvent,使用getSession()取得HttpSession,以针对会话对象做出相对应的创建或结束处理操作。

(2)HttpSessionAttributeListener:“属性改变监听器”,当在会话对象中加入属性、移除属性或替换属性时,相对应的attributeAdded()、attributeRemoved()与attributeReplaced()方法就会被调用,并分别传入HttpSessionBindingEvent。

public interface HttpSessionAttributeListener extends EventListener{
public void attributeAdded(HttpSessionBindingEvent e);
public void attributeRemoved(HttpSessionBindingEvent e);
public void attributeReplaced(HttpSessionBindingEvent e);
}


    HttpSessionBindingEvent有个getName()方法,可以取得属性设置或移除时指定的名称,而getValue()可以取得属性设置或移除时的对象。

(3)HttpSessionBindingListener:“对象绑定监听器”,如果有个即将加入HttpSession的属性对象,希望在设置给HttpSession成为属性或从HttpSession中移除时,可以收到HttpSession的通知,则可以让该对象实现HttpSessionBindingListener接口:

public interface HttpSessionBindingListener extends EventListener{
public void valueBound(HttpSessionBindingEvent event);
public void valueUnbound(HttpSessionBindingEvent event);
}
    这个接口即是实现加入HttpSession属性的对象,不需注释或在web.xml中设置,当实现此接口的属性对象被加入HttpSession或从中移除时,就回调用对应的valueBound()与valueUnbound()方法,并传入HttpSessionBindingEvent对象,可以通过该对象的getSession()取得HttpSession对象。在valueBound()中,可以实现查询数据库的功能(也许是委托给一个负责产寻数据库的服务对象),当HttpSession失效前会先移除属性,或者主动移除属性,则valueUnbound()方法会被调用。

(4)HttpSessionActivationListener:“对象迁移监听器”,其定义了两个方法sessionWillPassivate()与sessionDidActivate()方法。在使用到分布式环境时,应用程序的对象可能分散在多个JVM中,当HttpSession要从一个JVM迁移到另一个JVM时,必须在原先版本的JVM上序列化所有的属性对象,在这之前若属性对象实现HttpSessionActivationListener,就会调用sessionWillPassivate()方法,而HttpSession迁移至另一个JVM时,就会对所有属性对象做反序列化,此时会调用sessionDidActivate()方法。

3、HttpServletRequest事件、监听器

    与请求相关的监听器有三个:ServletRequestListener、ServletRequestAttributeListener与AsyncListener。

(1)ServletRequestListener:“生命周期监听器”,如果想要在HttpServletRequest对象生成或结束时做些相对应的操作,则可以实现ServletRequestListener。

public interface ServletRequestListener extends EventListener{
public void requestDestroyed(ServletRequestEvent sre);
public void requestInitialized(ServletRequestEvent sre);
}
    在ServletRequest对象初始化或结束前,会调用requestInitialized()与requestDestroyed()方法,可以传入ServletRequestEvent来取得ServletRequest,以针对请求对象做出相对应的初始化或结束处理动作。

(2)ServletRequestAttributeListener:“属性改变监听器”,在请求对象中加入属性、移除属性或替换属性时,相对应的attributeAdded()、attributeRemoved()与attributeReplaced()方法就会被调用,并分别传入ServletRequestAttributeEvent。ServletRequestAttributeEvent有个getName()方法,可以取得属性设置或移除时指定的名称,而getValue()则可以取得属性设置或移除时的对象。

    生命周期监听器与属性改变监听器都必须使用@WebListener标注或在web.xml中设置,容器才会知道要加载、读取监听器的相关设置。

三、过滤器

    过滤器是介于Servlet之前,可拦截浏览器对Servlet的请求,也可以改变Servlet对浏览器的响应。

1、过滤器的概念

    如性能测评、用户体验、字符替换、编码设置这类的需求,应该设计为独立的元件,随时可以加入应用程序中,也随时可以移除,或随时可以修改设置而不用修改原有的设置,这类元件就像是一个过滤器,安插在浏览器与Servlet之间,可以过滤请求与响应而做进一步的处理。Servlet/JSP提供了过滤器机制实现这些元件服务,可以视需求抽换过滤器或调整过滤器的顺序,也可以针对不同的URL应用不同的过滤器,设置在不同的Servlet间请求转发或包含时应用过滤器。

2、实现与设置过滤器

(1)在Servlet/JSP中要实现过滤器,必须实现Filter接口,并用@WebFilter标注或在web.xml中定义过滤器,让容器知道该加载哪些过滤器类,Filter接口有三个要实现的方法:init()、doFilter()与destroy()。

public interface Filter{
public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)throws IOException,ServletException;
public void destroy();
}
    FilterConfig类是实现Fileter接口的类上使用标注或web.xml中过滤器设置信息的代表对象,如果在定义过滤器时设置了初始参数,则可以通过FilterConfig的getInitParameter()方法来取得初始参数。

     Filter接口的doFilter()方法类似于Servlet接口的service()方法,当请求来到容器,而容器发现调用Servlet的service()方法前,可以应用某过滤器时,就会调用该过滤器的doFilter()方法,可以在doFilter()方法中进行service()方法的前置处理,而后决定是否调用FilterChain的doFilter()方法。

FilterChain的doFilter实现:
Filter filter=filterInterator.next();
if(filter!=null){
filter.doFilter(request,response,this);
}
else{
targetServlet.service(request,response);
}


    如果调用了FilterChain的doFilter()方法,就会运行下一个过滤器,如果没有下一个过滤器了,就调用请求目标Servlet的service()方法。如果因为某个情况(如用户没有通过验证)而没有调用FilterChain的doFilter()方法,则请求就不会继续交给接下来的过滤器或目标Servlet,这是就是所谓的拦截请求。

    在陆续调用完Filter实例的doFilter()仍至Servlet的service()方法之后,流程会以堆栈顺序返回,所以在FilterChain的doFilter()运行完毕后,就可以针对service()方法做后续处理。如果在调用Filter的doFilter()期间因故抛出UnavailableException,此时不会继续下一个Filter,容器可以检验异常的isPermanent(),如果不是true,则可以在稍后重试Filter。

3、请求封装器

(1)实现字符替换过滤器HttpServletRequestWrapper实现了HttpServletRequest接口,只要继承HttpServletRequestWrapper类,并编写想要重新定义的方法。

    EscapeWrapper类继承了HttpServletRequestWrapper,并定义了一个接收HttpServletRequest的构造器,真正的HttpServletRequest将通过此构造器传入,必须使用super()调用HttpServletRequestWrapper接受HttpServletRequest的构造器,之后如果要取得被封装的HttpServletRequest,则可以调用getRequest()方法,之后若有Servlet要取得请求参数,都会调用getParameter()方法。

(2)实现编码设置过滤器:可在过滤器中进行字符编码设置,如果日后要改变编码,就不用每个Servlet逐一修改设置。

    请求参数的编码设置是通过过滤器初始参数来设置的,并在过滤器初始化方法init()中读取,过滤器尽在GET请求以创建EncodingWrapper实例,其他方法则通过HttpServletRequest的setCharacterEncoding()设置编码,最后都调用FilterChain的doFilter()方法传入EncodingWrapper实例或原请求对象。

4、响应封装器

    继承HttpServletResponseWrapper类(父类ServletResponseWrapper)来对HttpServletResponse对象进行封装。

(1)若要对浏览器进行输出响应,必须通过getWriter()取得PrintWritter,或是通过getOutputStream()取得ServletOutputStream,所以针对压缩输出的请求,主要是继承HttpServletResponseWrapper之后,通过重新定义这两个方法来完成。

(2)GZIP是浏览器可以接收的压缩格式,可以使用GZIPOutputStream类来实现。GzipServletOutputStream继承ServletOutputStream类,使用时必须传入ServletOutputStream类,由GZIPOutputStream里增加压缩输出串流的功能。在HttpServletResponse对象传入Servlet的service()方法之前,必须封装它,使得调用getOutputStream()时,可以取得这里所实现的GzipServletOutputStream对象,而调用getWriter()时,也可以利用GzipServletOutputStream对象来构造PrintWritter对象。

(3)浏览器是否接收GZIP压缩格式,可以通过检查accept-encoding请求标头中是否包括gzip字符串来判断,如果可以接受GZIP格压缩,创建CompressWrapper封装原响应对象,并设置content-encoding响应标头为gzip,这样浏览器就会知道响应内容是GZIP压缩格式。如果客户端不接收GZIP压缩格式,则直接调用FilterChain的doFilter(),这样就可以让不接受GZIP格式的客户端也可以收到原有的响应内容。

四、异步处理

    Web容器会为每个请求分配一个线程,默认情况下,响应完成前,该线程占用的资源都不会被释放。若有些请求需要长时间处理(例如长时间运算、等待某个资源),就会长时间占用线程所需资源,若这类请求很多,许多线程资源都被长时间占用,会对系统的性能造成负担。异步处理可以先释放容器分配给请求线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在处理完成(例如长时间运算完成、所有资源已获得)时再对客户端进行响应。

1、AsyncContext简介

(1)为了支持异步处理,在ServletRequest上提供了startAsync()方法:

AsyncContext startAsync() throws java.lang.IllegalStateException;     //直接利用原有的请求与响应对象来创建AsyncContext
AsyncContext startAsync(ServletRequest servletRequest,ServletResponse servletResponse)throws java.lang.IllegalStateException;     //可以传入自行创建的请求、响应封装对象
     这两个方法都会返回AsyncContext接口的实现对象,在调用了startAsync()方法取得AsyncContext对象后,此次请求的响应会被延后,并释放容器分配的线程。

(2)可以通过AsyncContext的getRequest()、getResponse()方法取得请求、响应对象,此次对客户端的响应将暂缓至调用AsyncContext的complete()或dispatch()方法为止,前者表示响应完成,后者表示将调派指定的URL进行响应。

(3)若要能调用ServletRequest的startAsync()以取得AsyncContext,必须告知容器此Servlet支持异步处理,如果使用@WebServlet来标注,则可以设置其asyncSupported为true,如果使用web.xml设置Servlet,则可以在<servlet>中设置<async-supported>标签为true。

(4)如果Servlet将会进行异步处理,若其前端有过滤器,则过滤器需标示其支持异步处理,如果使用@WebFilter,可以设置其asyncSupported为true,如果使用web.xml设置过滤器,则可以设置<async-supported>标签为true。

(5)如果Servlet或过滤器的asyncSupported被标示为true,则它们支持异步请求处理,在不支持异步处理的Servlet或过滤器中调用startAsync(),会抛出IllegalStateException。当在支持异步处理的Servlet或过滤器中调用请求对象的startAsync()方法时,该次请求会离开容器所分配的线程,这意味着必须响应处理流程会返回,也就是若有过滤器,也会依序返回(也就是各自完成FilterChain的doFilter()方法),但最终的响应被延迟。

(6)可以调用AsyncContext的complete()方法完成响应,而后调用forward()方法,将响应转发给别的Servlet/JSP处理。将请求的响应权派送给别的页面来处理,给定的路径是相对于ServletContext的路径,不可以自行在同一个AsyncContext上同时调用complete()与forward(),否则会抛出IllegalStateException。

(7)不可以在两个异步处理的Servlet间派送前,连续调用两次startAsync(),否则会抛出IllegalStateException。

(8)将请求从支持异步处理的Servlet(asyncSupported被表示为true)派送至一个同步处理的Servlet是可行的(asyncSupported被标示为false)此时,容器会负责调用AsyncContext的complete()。

(9)如果从一个同步处理额Servlet派送至一个支持异步处理的Servlet,在异步处理的Servlet中调用AsyncContext的startAsync(),将会抛出IllegalStateException。

(10)AsyncContext有个addListener()方法,可以加入AsyncListener的实现对象,在对应时间发生时会调用AsyncListener实现对象的对应方法。

2、模拟服务器推播

异步处理技术可以解决每个请求占用线程的问题,若搭配浏览器端Ajax异步请求技术,就可以达到类似服务器端主动通知浏览器的行为,也就是所谓的服务器端推播。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: