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 {
    }
}

注:HandlerInterceptorAdapter在Spring 5.3版本被标记为废弃。

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

@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:6.0版本被废弃
  4. ResourceUrlProviderExposingInterceptor
  5. ConversionServiceExposingInterceptor
  6. UriTemplateVariablesHandlerInterceptor

Filter

jakarta.servlet.Filter是Java原有接口:

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);
}

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

配置

在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>

实战

  1. 统计当前在线人数

对比事件驱动

监听器模式和事件驱动模式都是软件设计中常见的模式,有共同点,也有区别:

  • 基本概念:
    • 监听器模式(Listener Pattern)是一种设计模式,用于在对象之间建立一种一对多的依赖关系,当某个对象的状态发生变化时,所有依赖于它的对象都会得到通知并做出相应的处理
    • 事件驱动模式(Event-Driven Pattern)是一种编程范式或设计思想,它基于事件和事件处理机制,通过事件的触发和监听来实现程序的响应和控制。
  • 角色和关系:
    • 监听器模式中包含两个主要角色:事件源(Event Source)和监听器(Listener)。事件源负责产生事件并通知监听器,监听器则负责接收并处理事件
    • 事件驱动模式中的关键是事件和事件处理器。事件是程序中发生的某种状态或行为,事件处理器是负责处理特定事件的代码块或函数。事件的产生和处理是通过事件触发和注册的方式实现的。
  • 实现方式:
    • 监听器模式通常通过接口或抽象类定义监听器,然后具体的监听器类实现这个接口或继承这个抽象类,并注册到事件源中。事件源在特定条件下会调用监听器的方法来通知事件发生。
    • 事件驱动模式通常通过事件、事件源、事件处理器等组件来实现。当事件源产生事件时,会将事件传递给注册的事件处理器进行处理。
  • 灵活性和复杂性:
    • 监听器模式可以使系统更加灵活,降低对象之间的耦合度,但也增加复杂性,因为需要维护事件源和监听器之间的关系。
    • 事件驱动模式可以使系统更加响应式和异步化,使得程序在事件发生时能够及时作出响应。也可降低对象之间的耦合度,但需要设计良好的事件和事件处理机制。

总的来说,监听器模式更侧重于对象之间的通信和依赖关系,通过监听器来实现对象之间的解耦和事件的传递;而事件驱动模式更侧重于程序的响应和控制,通过事件和事件处理机制来实现程序的异步响应和事件驱动。两者可以结合使用,如在GUI程序中常见的事件驱动模式就是监听器模式的一种应用。

区别

拦截器和过滤器

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

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

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

过滤器和监听器

都是用于增强Web应用的功能和扩展性的重要组件,但它们的作用对象、触发时机、接口实现以及用途有一定的差异

  • 作用对象:过滤器作用于Servlet、JSP或其他Web资源的请求和响应过程。它可以对请求进行预处理,也可以对响应进行后处理,例如修改请求、过滤请求、修改响应、过滤响应等。
    监听器则是用于监听Web应用中的事件,如ServletContext、HttpSession、ServletRequest等对象的创建、销毁、属性变更等事件。
  • 触发时机:过滤器在请求进入Servlet之前进行处理,也可以在响应返回给客户端之前进行处理,因此它可以用来实现诸如权限控制、字符编码转换、日志记录等功能。
    监听器则是在特定事件发生时触发,例如 ServletContext 初始化、销毁,HttpSession 创建、销毁,ServletRequest 属性变更等。
  • 接口实现:过滤器实现jakarta.servlet.Filter接口,重写doFilter()方法来处理请求和响应。
    监听器实现jakarta.servlet.ServletContextListenerjakarta.servlet.http.HttpSessionListenerjakarta.servlet.ServletRequestListener等接口,根据需要监听相应的事件。
  • 用途:过滤器常用于对请求和响应进行过滤处理,如设置字符编码、身份验证、日志记录等。
    监听器常用于监听Web应用中的事件,如初始化操作、销毁操作、属性变更等,用于执行特定的逻辑。

参考

java-listener

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

johnny233

晚饭能不能加鸡腿就靠你了

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值