『译』Java EE 六 Pocket.Guide—Servlets3.0(二)

『译』Java EE 6 Pocket.Guide—Servlets3.0(二)

  Servlet Filters(过滤器)

 

  Servlet Filter(过滤器)用于从Servlet取得和更新request(请求)、response(响应)的内容和头信息,要注意的是Filter不创建response或者像servlets那样响应request 。认证、日志、数据压缩、加密是使用过滤器一些典型用途。 过滤器包装了一个servlet并作用于动态或静态内容。
  过滤器可以与一个servlet或与一组servlets、或指定的URL的静态内容向关联。过滤器由注解@WebFilter所定义:

@WebFilter("/*")
public class LoggingFilter implements javax.servlet.Filter {
  public void doFilter(HttpServletRequest request,HttpServletResponse response) {
    //. . .
  }
}

   代码表示,该Loggingfilter是适用于这个web程序所有的servlet和静态网页,这里@WebInitParam同样可用于指定初始化参数。一个过滤器和目标servlet总是在同一调用线程执行,多个过滤器可形成一个过滤器链。在部署描述符中一个过滤器由<filter>和<filtermapping>元素来定义:

<filter>
  <filter-name>LoggingFilter</filter-name>
  <filter-class>org.sample.LoggingFilter</filter-class>
</filter>
. . .
<filter-mapping>
  <filter-name>LoggingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

   除了使用@WebFilter和web.xml来配置过滤器,也可以用ServletContext.addFilter()方法来编程化定义,在ServletContainerInitializer.onStartup()方法或者ServletContextListener.contextInitialized()方法执行时来起作用,addFilter()方法返回一个ServletRegistration,动态的用来添加匹配URL映射、设置初始化参数和其他配置项:

@HandlesTypes(value = { HttpServlet.class })
public class MyInitializer implements ServletContainerInitializer {
  public void onStartup(Set<Class<?>> clazz, ServletContext context) {
    FilterRegistration.Dynamic reg = context.addServlet("LoggingFilter","org.example.LoggingFilter");
    reg.addMappingForUrlPatterns(null, false, "/");
  }
}

  实现类需要添加一个HandlesTypes注解,这个注解有一个value值,这个值是一个Class数组,包含了你想要处理的类,容器会扫描出所有匹配这个数组里面的类型的类(包含实现,扩展或者被注解的类),然后将这种类型作为onStartup的第一个参数传入。

 

   Event Listeners(事件监听

 

  事件监听器提供ServletContext、HttpSession、ServletRequest对象的生命周期回调事件,监听器类实现一个接口,支持这些对象的状态变化的事件通知,通过注解@WebListener、在web.xml中声明,或通过注册一个ServletContext.addListener()方法来申明,监听器是不需要程序员额外明确编程化注册,或在初始化和恢复一个数据库连接去编程化的声明的一个servlet。可能有多个监听器类同时监听每一个事件类型,容器调用监听器时他们可能按指定的顺序监听每一个事件类型,应用程序关闭时以相反的顺序通知监听器。

  ServletContextListener监听容器的事件信息:

 

@WebListener
public class MyContextListener implements ServletContextListener {
  @Override
  public void contextInitialized(ServletContextEvent sce) {
    ServletContext context = sce.getServletContext();
    //. . .
  }
  @Override
  public void contextDestroyed(ServletContextEvent sce) {
  //. . .
  }
}

ServletContextAttributeListener监听上下文中属性的变化:

 

public class MyServletContextAttributeListener implements ServletContextAttributeListener {
  @Override
  public void attributeAdded(ServletContextAttributeEvent event) {
    //. . . event.getName();
    //. . . event.getValue();
  }
  @Override
  public void attributeRemoved(ServletContextAttributeEvent event) {
    //. . .
  }
  @Override
  public void attributeReplaced(ServletContextAttributeEvent event) {
    //. . .
  }
}

HttpSessionListener监听Session创建或销毁事件:

 

@WebListener
public class MySessionListener implements HttpSessionListener {
  @Override
  public void sessionCreated(HttpSessionEvent hse) {HttpSession session = hse.getSession();
  //. . .
  }
  @Override
  public void sessionDestroyed(HttpSessionEvent hse) {
  //. . .
  }
}

  HttpSessionActivationListener用于监听Session钝化或激活事件:

 

public class MyHttpSessionActivationListener implements HttpSessionActivationListener {
  @Override
  public void sessionWillPassivate(HttpSessionEvent hse) {
    // ... hse.getSession();
  }
  @Override
  public void sessionDidActivate(HttpSessionEvent hse) {
    // ...
  }
}

  HttpSessionAttributeListener用于监听Session中属性值变化事件:

 

public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {
  @Override
  public void attributeAdded(
    HttpSessionBindingEvent event) {
    HttpSession session = event.getSession();
    //. . . event.getName();
    //. . . event.getValue();
  }
  @Override
  public void attributeRemoved(
    HttpSessionBindingEvent event) {
    //. . .
  }
  @Override
  public void attributeReplaced(
    HttpSessionBindingEvent event) {
    //. . .
  }
}

  HttpSessionBindingListener用于监听对象绑定到Session或者解除绑定事件:

 

public class MyHttpSessionBindingListener implements HttpSessionBindingListener {
  @Override
  public void valueBound(HttpSessionBindingEvent event) {
    HttpSession session = event.getSession();
    //. . . event.getName();
    //. . . event.getValue();
  }
  @Override
  public void valueUnbound(HttpSessionBindingEvent event){
    //. . .
  }
}

  ServletRequestListener监听Request状态:

 

@WebListener
public class MyRequestListener implements ServletRequestListener {
  @Override
  public void requestDestroyed(ServletRequestEvent sre) {
    ServletRequest request = sre.getServletRequest();
    //. . .
  }
  @Override
  public void requestInitialized(ServletRequestEvent sre) {
    //. . .
  }
}

  ServletRequestAttributeListener用于监听Request属性的变化,同时还有AsyncListener,用于管理例如完成、超时、出错等异步事件。

  在除了使用@WebListener和web.xml声明监听器外,他们还可以用使用ServletContext.addListener()方法编程方式定义,可以在ServletContainerInitializer.onStartup()或ServletContextListener.contextInitialized()方法中做到,当应用程序启动ServletContext的ServletContainerInitializer.onStartup()方法被调用:

 

public class MyInitializer implements ServletContainerInitializer {
  public void onStartup(Set<Class<?>> clazz, ServletContext context) {
    context.addListener("org.example.MyContextListener");
  }
}

 

  Asynchronous Support

 

  服务器资源是宝贵的应谨慎使用。考虑一个Servlet,它有一个JDBC连接,可从连接池中等待接收JMS消息或从文件系统中读取的资源,等待为一个“longrunning”的过程完全恢复阻塞的线程而等着坐着,什么都不做,这不是你使用服务器资源的最佳方式。这里服务器可以通过异步处理的控制(或线程)来执行长期运行的过程,其他任务完成后返回容器,当长期运行过程的响应返回时,或在长期运行的进程可能被分派到一个新的资源后,再用同一个线程请求继续处理。

  这是一个典型的应用,需要长时间运行过程的聊天应用程序,需要明确地启用一个servlet的异步行为,可以通过在@ WebServlet添加asyncSupported属性实现:

 

@WebServlet(urlPatterns="/async", asyncSupported=true)
  public class MyAsyncServlet extends HttpServlet {
  //. . .
}

  它也可以在web.xml指定<async-supported>元素或ServletRegistration.setAsyncSupported(true)方式注册。异步处理就可以使用request的startasync()方法在一个单独的线程开始,这的方法返回一个AsyncContext,它代表了异步请求的执行上下文,异步请求可以通过调用AsyncContext.complete(explicit)或分发到另一个资源(隐式)去完成,容器完成异步请求的情况下然后调用后面的程序。让我们看一个长时间的进程完成的例子:

 

class MyAsyncService implements Runnable {
  AsyncContext ac;
  public MyAsyncService(AsyncContext ac) {
    this.ac = ac;
  }
  @Override
  public void run() {
    //. . .
    ac.complete();
  }
}

  此服务可能被调用doGet方法:

 

@Override
protected void doGet(HttpServletRequest request,HttpServletResponse response) {
  AsyncContext ac = request.startAsync();
  ac.addListener(new AsyncListener() {
  public void onComplete(AsyncEvent event) throws IOException {
    //. . .
  }
  public void onTimeout(AsyncEvent event) throws IOException {
    //. . .
  }
  //. . .
  });

  ScheduledThreadPoolExexcutor executor =new ScheduledThreadPoolExexcutor(10);
  executor.execut(new MyAsyncService(ac));
}

  在这段代码中,该请求被设为异步模式。AsyncListener为请求处理完成、超时,和其他必要的行为注册监听事件,长时间运行的服务是在一个单独的线程中调用,并调用AsyncContext.complete(),发送请求处理完成的信号。一个请求可以从一个异步servlet发送去同步,但其他方式是非法的。异步行为可在Servlet、过滤器等使用。