过滤器的使用

首先需要实现 Filter接口然后重写它的三个方法

  • init 方法:在容器中创建当前过滤器的时候自动调用

  • destory 方法:在容器中销毁当前过滤器的时候自动调用

  • doFilter 方法:过滤的具体操作

我们先引入 Maven 依赖,其中 lombok 是用来避免每个文件创建 Logger 来打印日志

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

我们首先实现接口,重写三个方法,对包含我们要求的四个请求予以放行,将其它请求拦截重定向至/online,只要在将MyFilter实例化后即可,我们在后面整合代码中一起给出。

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component; import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException; @Log4j2
public class MyFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("初始化过滤器");
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper((HttpServletResponse) response);
String requestUri = request.getRequestURI();
log.info("请求地址是:"+requestUri);
if (requestUri.contains("/addSession")
|| requestUri.contains("/removeSession")
|| requestUri.contains("/online")
|| requestUri.contains("/favicon.ico")) {
filterChain.doFilter(servletRequest, response);
} else {
wrapper.sendRedirect("/online");
}
} @Override
public void destroy() {
//在服务关闭时销毁
log.info("销毁过滤器");
}
}

拦截器

Java中的拦截器是动态拦截 action 调用的对象,然后提供了可以在 action 执行前后增加一些操作,也可以在 action 执行前停止操作,功能与过滤器类似,但是标准和实现方式不同。

  • 登录认证:在一些应用中,可能会通过拦截器来验证用户的登录状态,如果没有登录或者登录失败,就会给用户一个友好的提示或者返回登录页面,当然大型项目中都不采用这种方式,都是调单点登录系统接口来验证用户。

  • 记录系统日志:我们在常见应用中,通常要记录用户的请求信息,比如请求 ip,方法执行时间等,通过这些记录可以监控系统的状况,以便于对系统进行信息监控、信息统计、计算 PV、性能调优等。

  • 通用处理:在应用程序中可能存在所有方法都要返回的信息,这是可以利用拦截器来实现,省去每个方法冗余重复的代码实现。

使用拦截器

我们需要实现 HandlerInterceptor 类,并且重写三个方法:

  • preHandle:在 Controoler 处理请求之前被调用,返回值是 boolean类型,如果是true就进行下一步操作;若返回false,则证明不符合拦截条件,在失败的时候不会包含任何响应,此时需要调用对应的response返回对应响应。

  • postHandler:在 Controoler 处理请求执行完成后、生成视图前执行,可以通过ModelAndView对视图进行处理,当然ModelAndView也可以设置为 null。

  • afterCompletion:在 DispatcherServlet 完全处理请求后被调用,通常用于记录消耗时间,也可以对一些资源进行处理。

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; @Log4j2
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("【MyInterceptor】调用了:{}", request.getRequestURI());
request.setAttribute("requestTime", System.currentTimeMillis());
return true;
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
if (!request.getRequestURI().contains("/online")) {
HttpSession session = request.getSession();
String sessionName = (String) session.getAttribute("name");
if ("haixiang".equals(sessionName)) {
log.info("【MyInterceptor】当前浏览器存在 session:{}",sessionName);
}
}
} @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
long duration = (System.currentTimeMillis() - (Long)request.getAttribute("requestTime"));
log.info("【MyInterceptor】[{}]调用耗时:{}ms",request.getRequestURI(), duration);
}
}

监听器

监听器通常用于监听 Web 应用程序中对象的创建、销毁等动作的发送,同时对监听的情况作出相应的处理,最常用于统计网站的在线人数、访问量等。关注公众号互联网架构师回复2T可以获取更多系列 Spring Boot 教程。

监听器大概分为以下几种:

  • ServletContextListener:用来监听 ServletContext 属性的操作,比如新增、修改、删除。

  • HttpSessionListener:用来监听 Web 应用种的 Session 对象,通常用于统计在线情况。

  • ServletRequestListener:用来监听 Request 对象的属性操作。

监听器的使用

我们通过 HttpSessionListener来统计当前在线人数、ip等信息,为了避免并发问题我们使用原子int来计数。

ServletContext,是一个全局的储存信息的空间,它的生命周期与Servlet容器也就是服务器保持一致,服务器关闭才销毁。

request,一个用户可有多个;

session,一个用户一个;而servletContext,所有用户共用一个。所以,为了节省空间,提高效率,ServletContext中,要放必须的、重要的、所有用户需要共享的线程又是安全的一些信息。

因此我们这里用ServletContext来存储在线人数sessionCount最为合适。

import lombok.extern.log4j.Log4j2;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.util.concurrent.atomic.AtomicInteger; @Log4j2
public class MyHttpSessionListener implements HttpSessionListener { public static AtomicInteger userCount = new AtomicInteger(0); @Override
public synchronized void sessionCreated(HttpSessionEvent se) {
userCount.getAndIncrement();
se.getSession().getServletContext().setAttribute("sessionCount", userCount.get());
log.info("【在线人数】人数增加为:{}",userCount.get()); //此处可以在ServletContext域对象中为访问量计数,然后传入过滤器的销毁方法
//在销毁方法中调用数据库入库,因为过滤器生命周期与容器一致
} @Override
public synchronized void sessionDestroyed(HttpSessionEvent se) {
userCount.getAndDecrement();
se.getSession().getServletContext().setAttribute("sessionCount", userCount.get());
log.info("【在线人数】人数减少为:{}",userCount.get());
}
}

过滤器、拦截器、监听器注册

import com.anqi.tool.sanqi.filter.MyFilter;
import com.anqi.tool.sanqi.interceptor.MyInterceptor;
import com.anqi.tool.sanqi.listener.MyHttpRequestListener;
import com.anqi.tool.sanqi.listener.MyHttpSessionListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
MyInterceptor myInterceptor; @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor);
} /**
* 注册过滤器
* @return
*/
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new MyFilter());
filterRegistration.addUrlPatterns("/*");
return filterRegistration;
} /**
* 注册监听器
* @return
*/
@Bean
public ServletListenerRegistrationBean registrationBean(){
ServletListenerRegistrationBean registrationBean = new ServletListenerRegistrationBean();
registrationBean.setListener(new MyHttpRequestListener());
registrationBean.setListener(new MyHttpSessionListener());
return registrationBean;
}
}

测试

import com.anqi.tool.sanqi.listener.MyHttpSessionListener;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession; @RestController
public class TestController { @GetMapping("addSession")
public String addSession(HttpServletRequest request) {
HttpSession session = request.getSession();
session.setAttribute("name", "haixiang");
return "当前在线人数" + session.getServletContext().getAttribute("sessionCount") + "人";
} @GetMapping("removeSession")
public String removeSession(HttpServletRequest request) {
HttpSession session = request.getSession();
session.invalidate();
return "当前在线人数" + session.getServletContext().getAttribute("sessionCount") + "人";
} @GetMapping("online")
public String online() {
return "当前在线人数" + MyHttpSessionListener.userCount.get() + "人";
}
}

以下是监听请求的监听器

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest; public class MyHttpRequestListener implements ServletRequestListener { @Override
public void requestDestroyed(ServletRequestEvent sre) {
System.out.println("request 监听器被销毁");
} @Override
public void requestInitialized(ServletRequestEvent sre) {
HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
String requestURI = req.getRequestURI();
System.out.println(requestURI+"--"+"被调用");
}
}

拦截器与过滤器的区别

1.参考标准

  • 过滤器是 JavaEE 的标准,依赖于 Servlet 容器,生命周期也与容器一致,利用这一特性可以在销毁时释放资源或者数据入库。

  • 拦截器是SpringMVC中的内容,依赖于web框架,通常用于验证用户权限或者记录日志,但是这些功能也可以利用 AOP 来代替。

2.实现方式

  • 过滤器是基于回调函数实现,无法注入 ioc 容器中的 bean。

  • 拦截器是基于反射来实现,因此拦截器中可以注入 ioc 容器中的 bean,例如注入 Redis 的业务层来验证用户是否已经登录。

Spring Boot 如何使用拦截器、过滤器、监听器的更多相关文章

  1. spring boot 实现mybatis拦截器

    spring boot 实现mybatis拦截器 项目是个报表系统,服务端是简单的Java web架构,直接在请求参数里面加了个query id参数,就是mybatis mapper的query id ...

  2. spring boot中注册拦截器

    拦截器是动态拦截Action调用的对象.它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行,同时也提供了一种可以提取action中可重 ...

  3. Spring Boot 如何使用拦截器、过滤器、监听器?

    过滤器 过滤器的英文名称为 Filter, 是 Servlet 技术中最实用的技术. 如同它的名字一样,过滤器是处于客户端和服务器资源文件之间的一道过滤网,帮助我们过滤掉一些不符合要求的请求,通常用作 ...

  4. Spring MVC中的拦截器/过滤器HandlerInterceptorAdapter的使用

    一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的 而在Spring中,基于Filter这种方式可以实现Bean预处理.后处理. 比如注入FilterRegistrationBean,然后 ...

  5. Spring Boot实战:拦截器与过滤器

    一.拦截器与过滤器 在讲Spring boot之前,我们先了解一下过滤器和拦截器.这两者在功能方面很类似,但是在具体技术实现方面,差距还是比较大的.在分析两者的区别之前,我们先理解一下AOP的概念,A ...

  6. ava中拦截器 过滤器 监听器都有什么区别

    过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts2的action进行业务逻辑,比如过滤掉非法u ...

  7. spring boot配置springMVC拦截器

    spring boot通过配置springMVC拦截器 配置拦截器比较简单, spring boot配置拦截器, 重写preHandle方法. 1.配置拦截器: 2重写方法 这样就实现了拦截器. 其中 ...

  8. java中拦截器 过滤器 监听器都有什么区别

    过滤器,是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts2的action进行业务逻辑,比如过滤掉非法u ...

  9. Spring Boot下使用拦截器

    Spring Boot对于原来在配置文件配置的内容,现在全部体现在一个类中,该类需要继承自WebMvcConfigurationSupport类,并使用@Configuration进行注解,表示该类为 ...

  10. spring boot 2.x拦截器导致静态资源404终极解决办法

    首先添加application文件static路径,我的是yml文件 spring: mvc: static-path-pattern: /static/**然后注册拦截器类如下方法; @Overri ...

随机推荐

  1. 4-11.3 C++中的显式类转换

    目录 static_cast 1.用来改变基本类型:一般是高精度转低精度 2.恢复void* 指针类型 const_cast 用来去除底层const(low-level const) reinterp ...

  2. UE4纯C++实现游戏快捷栏之将快捷栏注册到玩家状态

    我们有了UI有了物品信息,接下来我们便需要给每一个玩家绑定一个快捷栏了,我们分以下几部分来注册我们玩家的快捷栏. 1.Types.h:定于ShortcutContainer类,定义快捷栏的单个容器结构 ...

  3. 第三篇:低功耗模组Air724UG硬件设计手册

    ​ 今天我们分享最后一篇. 3.20 省电功能 根据系统需求,有两种方式可以使模块进入到低功耗的状态.对于AT版本使用"AT+CFUN"命令可以使模块 进入最少功能状态. 具体的功 ...

  4. 服务器cli模式下的定时器

    有时候需要一部分后台业务异步的处理,比如处理redis队列啊,处理mysql统计啊,数据同步啊,这种长时间任务,但是又需要每段时间去看看的,发现linux cron最小只能支持每分钟的任务 分 时 天 ...

  5. SyntaxError: non-default argument follows default argument

    有时候没注意就会犯一些低级错误 问题原因 把没有默认值的形参放到了带有默认值的形参后面 解决办法 问了一下chatgpt,果然一语道破.因为python在定义函数时,在不定长参数中,默认值参数不能放在 ...

  6. configuration file routines:DEF_LOAD_BIO:missing equal sign:conf_def.c:362

    报错: Auto configuration failed 139868431284128:error:0E079065:configuration file routines:DEF_LOAD_BI ...

  7. Redis之常用模块Module

    1. Redis-Cell 限流模块 2.RedisBloom 布隆过滤器 https://github.com/RedisBloom/RedisBloom

  8. sql 依据时间间隔分组,获取第一条数据

    时序数据的数据量比较大,抛去异常点外,变化相对比较有线性规律,业务上需要对结果进行抽取显示. 原始数据时序标签(部分示例) 根据时间字段,计算时间字段和指定时间的时间戳差值,然后除以固定间隔(示例中间 ...

  9. Fiddler模拟网络超时

    前情 最近在优化接口请求错误的报错提示,希望尽可能的能从提示语知道当前错误大致原因,于是我需要模拟各种错误请求的状况. 问题 网络超时是很常见的接口请求错误情况,在没有服务端配合的情况下,我需要怎样来 ...

  10. python雪花算法

    雪花算法(Snowflake Algorithm)是一种用于生成唯一的ID的算法,它由Twitter开发.其生成的ID在全局范围内是唯一的,适合高并发场景.雪花算法生成的ID通常是一个64位的整数,包 ...