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的常用拦截器类,如:
- UserRoleAuthorizationInterceptor,实现用户登录认证的拦截功能,如果当前用户没有通过认证,会报403错误
- LocaleChangeInterceptor
- ThemeChangeInterceptor:6.0版本被废弃
- ResourceUrlProviderExposingInterceptor
- ConversionServiceExposingInterceptor
- 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
- Authentication Filters,即用户访问权限过滤
- Logging and Auditing Filters,日志过滤,可以记录特殊用户的特殊请求的记录等
- Image conversion Filters
- Data compression Filters
- Encryption Filters
- Tokenizing Filters
- Filters that trigger resource access events
- XSL/T filters
- 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
文件,配置方法:
- 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;
}
}
- 注解:
// @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对象的方法调用或者属性变化。常用的监听器接口有三类:
- 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);
}
- 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);
}
- 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>
实战
- 统计当前在线人数
对比事件驱动
监听器模式和事件驱动模式都是软件设计中常见的模式,有共同点,也有区别:
- 基本概念:
- 监听器模式(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.ServletContextListener
、jakarta.servlet.http.HttpSessionListener
、jakarta.servlet.ServletRequestListener
等接口,根据需要监听相应的事件。 - 用途:过滤器常用于对请求和响应进行过滤处理,如设置字符编码、身份验证、日志记录等。
监听器常用于监听Web应用中的事件,如初始化操作、销毁操作、属性变更等,用于执行特定的逻辑。