过滤器对 Servlet 容器调用 service 的过程进行拦截,其基本使用场景如下:
统一设置字符编码
过滤敏感字符
进行登录校验以及 url 级别的访问权限控制
拦截器本质上是面向切面编程 AOP,符合切面编程的关注点都可以通过拦截器来实现,其基本使用场景如下:
登陆验证以及权限验证
进行日志记录
对 cookie 进行处理
性能监控,记录请求处理时长
构造过滤器,实现Filter
接口,编写过滤逻辑
/**
* 自定义过滤器1
*
* @author zqf
*/
public class MyFilter1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 进入过滤器前处理逻辑
System.out.println("===过滤器1前处理===");
// 执行 doFilter() 方法
filterChain.doFilter(servletRequest, servletResponse);
// Controller 返回之后,客户端返回之前处理逻辑
System.out.println("===过滤器1后处理===");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
配置过滤器,对过滤器的过滤请求路径以及执行顺序进行配置,此处配置了两个过滤器,过滤逻辑完全一致
/**
* 配置过滤器
*
* @author zqf
*/
@Configuration
public class FilterConfig {
/**
* 过滤路径
*/
private static final String[] URLS = new String[]{"/test/filter", "/test/all"};
@Bean("myFilter1")
public FilterRegistrationBean<Filter> myFilter1() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>(new MyFilter1());
// 配置要应用过滤器的路径,若存在多个则传入 String[]
registrationBean.addUrlPatterns(URLS);
// 设置执行顺序,数值越小,越先执行,服务器返回客户端时则相反
registrationBean.setOrder(1);
return registrationBean;
}
@Bean("myFilter2")
public FilterRegistrationBean<Filter> myFilter2() {
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>(new MyFilter2());
registrationBean.addUrlPatterns(URLS);
registrationBean.setOrder(2);
return registrationBean;
}
}
构造拦截器实现HandlerInterceptor
接口,编写拦截逻辑
/**
* 自定义拦截器1
*
* @author zqf
*/
public class MyInterceptor1 implements HandlerInterceptor {
/**
* 在 controller 执行之前进行拦截,返回结果将决定后续操作是否执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 编写拦截器逻辑
System.out.println("===拦截器1前处理===");
return true;
}
/**
* 在执行完 controller 的方法后进行拦截,可对 model 和 view 进行修改
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("===拦截器1后处理===");
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
/**
* 在整个请求完成之后执行,即视图渲染之后执行,若返回 json 数据则不会执行此方法
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
配置拦截器实现WebMvcConfigurer
接口,配置拦截路径以及拦截顺序,此处同样配置了两个拦截逻辑相同的拦截器
/**
* 配置拦截器
*
* @author zqf
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截路径
String[] urls = new String[]{"/test/interceptor", "/test/all"};
// 配置自定义的拦截器,配置路径以及顺序
registry.addInterceptor(new MyInterceptor1()).addPathPatterns(urls).order(1);
registry.addInterceptor(new MyInterceptor2()).addPathPatterns(urls).order(2);
}
}
编写控制器进行测试
/**
* @author zqf
*/
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/filter")
public String testFilter() {
System.out.println("===执行 controller===");
System.out.println("===调用 service 进行业务处理===");
System.out.println("===执行 return===");
return "测试过滤器~";
}
@GetMapping("/interceptor")
public String testInterceptor() {
System.out.println("===执行 controller===");
System.out.println("===调用 service 进行业务处理===");
System.out.println("===执行 return===");
return "测试拦截器~";
}
@GetMapping("/all")
public String testAll() {
System.out.println("===执行 controller===");
System.out.println("===调用 service 进行业务处理===");
System.out.println("===执行 return===");
return "测试过滤器and拦截器~";
}
}
过滤器 Filter 测试结果
拦截器 Interceptor 测试结果
同时测试,验证过滤器和拦截器的执行顺序
监听器是 Servlet 中的类,可以帮助我们监听 web 中的特定事件,比如ServletContext
、HttpSession
、ServletRequest
的创建和销毁或者变量的创建、销毁和修改,其使用场景主要包括:
监听 Servlet 进行数据的初始化
监听 HttpSession 获取实时在线人数
监听客户端请求的 ServletRequest 对象以获取用户的访问信息
新建一个 User 实体类,模拟从数据库查询到数据的过程
/**
* 实体类
*
* @author zqf
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String username;
private String password;
}
构造自定义事件继承ApplicationEvent
/**
* 自定义事件
*
* @author zqf
*/
public class MyEvent extends ApplicationEvent {
private User user;
public MyEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() {
return user;
}
}
构造自定义监听器继承ApplicationListener
,编写监听事件发生后的处理逻辑
/**
* 自定义事件监听器
*
* @author zqf
*/
@Component
public class MyListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent myEvent) {
// 监听到相应事件后的处理逻辑
User user = myEvent.getUser();
System.out.println("监听到事件发生>>> User 对象创建");
System.out.println("username>>> " + user.getUsername());
System.out.println("password>>> " + user.getPassword());
}
}
利用 SpringBoot 定时任务定时发布事件,查看监听器监听到事件后的处理,记得在启动类上允许定时任务@EnableScheduling
/**
* @author zqf
*/
@Service
public class ListenerService {
@Resource
private ApplicationContext applicationContext;
/**
* 定时任务模拟时间发生进行测试
*
* 5s 执行一次
*/
@Scheduled(cron = "*/10 * * * * ?")
public void testListenerEvent() {
// 模拟事件发生
System.out.println("模拟事件发生>>> User 对象创建");
User user = User.builder()
.username("test" + Math.random())
.password("[email protected]")
.build();
// 手动发布事件
MyEvent myEvent = new MyEvent(this, user);
applicationContext.publishEvent(myEvent);
}
}
监听器测试结果