Java web之拦截器Interceptor、过滤器Filter以及监听器Listener

Java web开发必知的三大器

Interceptor

拦截器是基于Java反射机制(动态代理)来实现的;可以控制请求的控制器和方法,但控制不了请求方法里的参数(用于处理页面提交的请求响应并进行处理,例如国际化,主题更换,过滤等)。

一般说到拦截器都是基于Spring框架下,自定义拦截器可以实现HandlerInterceptor接口或继承抽象类HandlerInterceptorAdapter,并重写3个方法即可。HandlerInterceptor源码:

public interface HandlerInterceptor {
	// preHandle请求执行前执行
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
	// postHandler请求结束后执行,需preHandle方法返回true才执行
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }
	// afterCompletion是视图渲染完成后才执行,需preHandle返回true,常用于清理资源等工作
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

抽象类HandlerInterceptorAdapter实际上是实现AsyncHandlerInterceptor,然后AsyncHandlerInterceptor继承HandlerInterceptor,并增加方法afterConcurrentHandlingStarted,其源码:

public interface AsyncHandlerInterceptor extends HandlerInterceptor {
	//
    default void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    }
}

无论是哪种方式,都需要添加配置使之生效:

@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	// DemoInterceptor
        registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
}

另外Spring框架提供很多继承HandlerInterceptorAdapter的常用拦截器类,如:

  1. UserRoleAuthorizationInterceptor,实现用户登录认证的拦截功能,如果当前用户没有通过认证,会报403错误
  2. LocaleChangeInterceptor
  3. ThemeChangeInterceptor
  4. ResourceUrlProviderExposingInterceptor
  5. ConversionServiceExposingInterceptor
  6. UriTemplateVariablesHandlerInterceptor

Filter

接口:

public interface Filter {
	default void init(FilterConfig filterConfig) throws ServletException {
	}	
	void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
	default void destroy() {
	}
}

FilterChain,把所有的过滤器都放在FilterChain里边,责任链模式。JavaDoc给出几种过滤器的作用:

Examples that have been identified for this design are

  1. Authentication Filters,即用户访问权限过滤
  2. Logging and Auditing Filters,日志过滤,可以记录特殊用户的特殊请求的记录等
  3. Image conversion Filters
  4. Data compression Filters
  5. Encryption Filters
  6. Tokenizing Filters
  7. Filters that trigger resource access events
  8. XSL/T filters
  9. Mime-type chain Filter

示例

延迟加载过滤器

Hibernate允许对关联对象、属性进行延迟加载,但是必须保证延迟加载的操作限于同一个 Hibernate Session 范围之内进行。
如果 Service 层返回一个启用延迟加载功能的领域对象给 Web 层,当 Web 层访问到那些需要延迟加载的数据时,由于加载领域对象的 Hibernate Session 已经关闭,这些导致延迟加载数据的访问异常。
Spring 为此专门提供一个 OpenSessionInViewFilter 过滤器,它的主要功能是使每个请求过程绑定一个 Hibernate Session,即使最初的事务已经完成,也可以在 Web 层进行延迟加载的操作。

<filter>
    <filter-name>hibernateFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>hibernateFilter</filter-name>
    <url-pattern>*.html</url-pattern>
</filter-mapping>

中文乱码过滤器

<filter>
	<filter-name>encodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
	<init-param>
	    <param-name>encoding</param-name>
	    <param-value>UTF-8</param-value>
	</init-param>
	<init-param>
	    <param-name>forceEncoding</param-name>
	    <param-value>true</param-value>
	</init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>*.html</url-pattern>
</filter-mapping>

其他功能如:记录请求执行时间

配置

在SB应用中,没有web.xml文件,配置方法:

  1. FilterRegistrationBean
@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean registFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        // LogCostFilter实现Filter接口
        registration.setFilter(new LogCostFilter());
        registration.addUrlPatterns("/*");
        registration.setName("LogCostFilter");
        registration.setOrder(1);
        return registration;
    }
}
  1. 注解:
// @WebFilter,Servlet3.0规范,还需在启动类加注解@ServletComponetScan指定扫描包
@WebFilter(urlPatterns = "/*", filterName = "logFilter2")
public class LogCostFilter2 implements Filter {
	// 省略3个方法的实现
}

@WebFilter指定的过滤器优先级都高于FilterRegistrationBean配置的过滤器。@WebFilter不支持设置执行顺序,其顺序根据Filter类名的字母顺序倒序排列。

Listener

EventListener空实现,标记接口。

/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

监听器就是一个实现特定接口的程序。用于监听一个Java对象的方法调用或者属性变化。常用的监听器接口有三类:

  1. Application级
    对Servlet上下文进行监听,用于监听ServletContext对象的创建和删除以及属性的添加、删除、修改等操作,有两个接口类:
  • ServletContextAttributeListener源码:
public interface ServletContextAttributeListener extends EventListener {
	// 当程序把一个属性存入application范围时触发该方法
    void attributeAdded(ServletContextAttributeEvent var1);
    // 当程序把一个属性从application范围删除时触发该方法
    void attributeRemoved(ServletContextAttributeEvent var1);
    // 当程序替换application范围内的属性时将触发该方法
    void attributeReplaced(ServletContextAttributeEvent var1);
}
  • ServletContextListener,当需要在处理任何客户端请求之前进行某个操作,并且希望在整个应用过程中该操作一直可用,此时ServletContextListener接口将会起到作用。其源码:
public interface ServletContextListener extends EventListener {
	// 创建ServletContext时,即Web应用程序初始化后激发该方法
    void contextInitialized(ServletContextEvent var1);
    // 销毁ServletContext时,即将结束销毁激发该方法
    void contextDestroyed(ServletContextEvent var1);
}
  1. Session级
    对HTTP会话进行监听,包括:session的创建和销毁,session中属性的增加、删除、修改,session的active和passivate情况等,接口主要有4个:
  • HttpSessionAttributeListener
    该接口用于监听HttpSession(session)范围内属性的改变,源码:
public interface HttpSessionAttributeListener extends EventListener {
	// 当在session中添加对象时触发此操作
    void attributeAdded(HttpSessionBindingEvent var1);
	// 当在session中删除对象时触发此操作
    void attributeRemoved(HttpSessionBindingEvent var1);
	// 当在session中修改对象时触发此操作
    void attributeReplaced(HttpSessionBindingEvent var1);
}
  • HttpSessionListener
    该接口用于监听session的创建和销毁过程,源码:
public interface HttpSessionListener extends EventListener {
	// 用户与服务器的会话开始、创建时时触发该方法
    void sessionCreated(HttpSessionEvent var1);
    // 用户与服务器的会话断开、销毁时触发该方法
    void sessionDestroyed(HttpSessionEvent var1);
}

一般情况下,HttpSessionActivationListener和HttpSessionBindingListener一起使用,这两个监听器比较特殊,实现这两个接口的类不需要在web.xml中进行注册,被钝化的JavaBean对象会被持久化到存储设备中,活化的JavaBean对象会被从存储设备中恢复,前提是该JavaBean对象实现Serializable接口。

  • HttpSessionActivationListener
    监听Active、unactive的事件,源码:
public interface HttpSessionActivationListener extends EventListener {
	// 当绑定到HttpSession对象中的对象将要随HttpSession对象被钝化之前,web服务器调用该对象的此方法
    void sessionWillPassivate(HttpSessionEvent var1);
	// 当绑定到HttpSession对象中的对象将要随HttpSession对象被活化之后,web服务器调用该对象的此方法
    void sessionDidActivate(HttpSessionEvent var1);
}
  • HttpSessionBindingListener
    监听被绑定到Session中和从Session中删除的事件:
public interface HttpSessionBindingListener extends EventListener {
	// 当对象被绑定到HttpSession对象中时,web服务器调用该对象的此方法,从而对象被设置到session中 
    void valueBound(HttpSessionBindingEvent var1);
    // 当对象从HttpSession对象中解除绑定时,web服务器调用该对象的此方法,从而对象从session中被移除
    void valueUnbound(HttpSessionBindingEvent var1);
}
  1. request级
    对客户端请求进行监听,监听用户的请求和request范围内属性的变化,接口主要有2个:
  • ServletRequestAttributeListener源码:
// 用于监听request范围内属性的变化
public interface ServletRequestAttributeListener extends EventListener {
	// 当程序向request范围内添加属性时触发该方法
    void attributeAdded(ServletRequestAttributeEvent var1);
    // 当程序在request范围内删除属性时触发该方法
    void attributeRemoved(ServletRequestAttributeEvent var1);
    // 当程序在request范围内的属性被替换或修改时触发该方法
    void attributeReplaced(ServletRequestAttributeEvent var1);
}
  • ServletRequestListener源码:
public interface ServletRequestListener extends EventListener {
	// 用户请求到达、被初始化时触发该方法
    void requestDestroyed(ServletRequestEvent var1);
    // 用户请求结束、被销毁时触发该方法
    void requestInitialized(ServletRequestEvent var1);
}

原理:
在这里插入图片描述

实例

  1. 统计当前在线人数

在Servlet 3.0之前,监听器一般需要在web.xml中进行注册:

<listener>
    <listener-class>aa.bb.DemoContextAttrListener</listener-class>
</listener>

3.0之后,提供注解@WebListener,但是没有设置初始参数的属性,所以仅适用于无须设置初始参数的情况。

和事件驱动的区别??

一般情况下,必须将 Log4J 日志配置文件以 log4j.properties 为文件名并保存在类路径下。
Log4jConfigListener 允许您通过 log4jConfigLocation Servlet上下文参数显式指定 Log4J 配置文件的地址:

<-- 指定 Log4J 配置文件的地址 -->
<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<-- 使用该监听器初始化 Log4J 日志引擎 -->
<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

Introspector 缓存清除监听器,防止内存泄露

<listener>
    <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>

区别

拦截器和过滤器的区别

功能比较类似,过滤器和拦截器都是AOP的具体实现。区别:

  • 拦截器是基于Java反射(动态代理)机制,过滤器是基于函数回调;
  • 拦截器不依赖与servlet容器,过滤器依赖于servlet容器;
  • 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用;
  • 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问;
  • 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次;
  • 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,在拦截器里注入一个service,可以调用业务逻辑;
  • 拦截器只能过滤请求,过滤器过滤范围较大;

使用的主要是函数回调,和框架无关,可以控制最初的http请求,但是更细一点的类和方法控制不了。
一个请求过来,先由过滤器处理,看程序是否受理该请求。过滤器放过后,程序中的拦截器进行处理, 处理完后进入被AOP动态代理重新编译过的主要业务类进行处理。
Filter,Interceptor,Aspect 实际上都是对Aop的具体实现。都是对业务逻辑的提取。都可以实现权限检查,日志记录。不同的是使用的范围不同,规范不同,深度不同。

参考

java-listener

posted @ 2020-05-05 17:23  johnny233  阅读(55)  评论(0编辑  收藏  举报  来源