最近接到一个任务,是和同事分享一下springboot如何集成监听器,过滤器,拦截器的。看了一些博客,自己本地跑了几个demo,记录一下,加深记忆,如有错误,请不吝指教
1. 监听器
监听器:listener是servlet规范中定义的一种特殊类。用于监听servletContext、HttpSession和servletRequest等域对象的创建和销毁事件。监听域对象的属性发生修改的事件。用于在事件发生前、发生后做一些必要的处理。其主要可用于以下方面:1、统计在线人数和在线用户2、系统启动时加载初始化信息3、统计网站访问量4、记录用户访问路径。
代码实现如下:
package cn.newhope.de.listener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class MyListener implements HttpSessionListener {
public static int online = 0;
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("创建session,在线用户数:" + (++online));
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("销毁session,在线用户数:" + (--online));
online--;
}
}
创建一个类继承想要监听的监听器接口,然后实现对应方法,在类上面加注解 @WebListener ,然后在主程序上加注解@ServletComponentScan配置监听器所在包路径。
2.过滤器
过滤器:Filter是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
代码实现如下:
package cn.newhope.de.filter;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "myfilter2",urlPatterns = "/*")
public class MyFilter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("这是filter2");
//
chain.doFilter(request,response);
}
}
上面的实现方式是通过注解的方式实现,通过上面这种方式实现的过滤器需要注意和监听器一样在启动类上面加@ServletComponentScan注解,否则会扫描不到
注解方式实现的过滤器有两点需要说明:
- 当有多个过滤器需要实现时,就要考虑顺序的问题,但是这种注解方式实现的过滤器没有提供设置顺序的api,网上说加@order注解可实现配置顺序,但是亲测无效,也可能是我姿势不对,可自行尝试
- 这种通过注解方式实现的过滤器,有多个过滤器时,他有一个默认的排序规则是根据你的过滤器类名字典序排列,所以另一种排序措施是设置有规律的过滤器类名字,比如filter0_filter,filter1_filter,filter2_filter,这种实现方式有个弊端是如果你想中间加一个过滤器的话,可能要重新排序或者修改命名规则,不推荐
过滤器另一种实现方式是通过代码实现,如下
package cn.newhope.de.config;
import cn.newhope.de.filter.MyFilter2;
import cn.newhope.de.filter.MyFilter3;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 过滤器配置类
*/
@Configuration
public class FilterConfigBean {
/**
* 注解方式实现的过滤器如果多个的话对名字有要求
* 如果想自定义名字和顺序,可在这个类添加方法实现
*
*/
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean =
new FilterRegistrationBean();
filterRegistrationBean.setFilter(new MyFilter2());
//指定过滤器的执行顺序
filterRegistrationBean.setOrder(2);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean2() {
FilterRegistrationBean filterRegistrationBean =
new FilterRegistrationBean();
filterRegistrationBean.setFilter(new MyFilter3());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
加一个配置类,每次新的过滤器过来添加一个bean,即使重排序改动也不会很大,推荐使用
3.拦截器
拦截器:Interceptor 在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。比如日志,安全等。一般拦截器方法都是通过动态代理的方式实现。可以通过它来进行权限验证,或者判断用户是否登陆,或者是像12306 判断当前时间是否是购票时间。
代码实现如下:
package cn.newhope.de.interceptor;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
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;
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle.........");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle.........");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion.........");
}
}
//配置类代码
package cn.newhope.de.config;
import cn.newhope.de.interceptor.MyInterceptor;
import cn.newhope.de.interceptor.MyInterceptor2;
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 InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/hello").order(1);
registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/redis").order(2);
//.excludePathPatterns("/api2/xxx/**"); //拦截全部 /*/*/**
WebMvcConfigurer.super.addInterceptors(registry);
}
}
拦截器这边如果需要也涉及到顺序的问题,可以直接后面跟着设置order,没有的话可不加
4.关于PathPatterns
上面的过滤器和拦截器都涉及到根据路径做一些操作,那么如果针对特定路径的话,我们这个路径怎么配置呢?
关于拦截器的路径设置规则
5.题外话
我们对外提供的接口有时候可能出现莫名的调用,但是又不知道调用者是谁,这时候我们可以在服务入口处,即controller处加一个切面,获取request的ip,路径等信息,这样下次有请求过来可以通过ip追踪是否是正常的内部调用请求等,当然肯定不止这一种用途,这就看大家自由发挥了
package cn.newhope.de.aop;
import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.alibaba.fastjson.JSON;
@Aspect
@Component
public class WebLogAspect {
private final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
/**
* 切入点描述 这个是controller包的切入点
*/
@Pointcut("execution(public * cn.newhope.de.controller..*.*(..))")
public void controllerLog(){}//签名,可以理解成这个切入点的一个名称
/**
* 在切入点的方法run之前要干的
* @param joinPoint
*/
@Before(value = "controllerLog()")
public void logBeforeController(JoinPoint joinPoint) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//这个RequestContextHolder是Springmvc提供来获得请求的东西
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
String sessionId = request.getSession().getId();
// 记录下请求内容
logger.info("[{}]URL : " + request.getRequestURL().toString(), sessionId);
logger.info("[{}]HTTP_METHOD : " + request.getMethod(), sessionId);
logger.info("[{}]IP : " + request.getRemoteAddr(), sessionId);
logger.info("[{}]Request : " + Arrays.toString(joinPoint.getArgs()), sessionId);
}
/**
* 在方法执行完结后打印返回内容
* @param joinPoint
* @param o
*/
@AfterReturning(returning = "o",pointcut = "controllerLog()")
public void methodAfterReturing(JoinPoint joinPoint, Object o ){
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();//这个RequestContextHolder是Springmvc提供来获得请求的东西
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
String sessionId = request.getSession().getId();
logger.info("[{}]Response ::"+JSON.toJSONString(o), sessionId);
}
}
以上
感谢阅读,如有错误,请不吝指正