通俗易懂的解释:
过滤器:像滤网一样,肯定是用来过滤东西的,比如,三个请求只放行两个。
拦截器:拦而不截,都可以过,但是要过去的东西做点操作,比如,三个请求都放行。
具体区别:
过滤器 | 拦截器 | Aspect | |
---|---|---|---|
关注的点 | 所有web请求 | 部分web请求 | 偏向于业务层面的拦截 |
实现原理 | 函数回调 | JAVA反射机制(动态代理) | 动态代理 |
是否依赖servlet容器 | 依赖 | 不依赖 | 不依赖 |
servlet提供的支持 | Filter接口 | 无 | 无 |
spring提供的支持 | 无 | HandlerInterceptorAdapter类、HandlerInterceptor接口 | @Aspect |
作用级别 | 系统级 | 非系统级 | 皆可 |
可能需要实现的方法 | doFilter | preHandle、postHandle、afterCompletion | Before、After、Around |
本质区别:
从灵活性上说拦截器功能更强大些,Filter能做的事情,他都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。但是Aspect能够做到方法级更细的操作。
具体应用场景:
拦截器:
preHandle在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制等处理,也可以配合postHandle可以测某个方法的运行时间,不过arthas工具可以测方法的运行效率,详细可以看下代码排查利器Arthas介绍
postHandle在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView;
afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面),可以根据ex是否为null判断是否发生了异常,进行日志记录;
过滤器:
统一字符编码设置,实现跨域请求,登录日志记录,登录校验,解决需要多次读取Request中携带的数据流,但是默认的只能读取一次的问题(https://www.cnblogs.com/JAYIT/p/10943155.html)
Aspect:
主要用于方法运行前后日志的插入。
代码展示:
拦截器:
//在这个例子中,我们假设UserController中的注册操作只在9:00-12:00开放,那么就可以使用拦截器实现这个功能。
public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
private int openingTime;
private int closingTime;
private String mappingURL;//利用正则映射到需要拦截的路径
public void setOpeningTime(int openingTime) {
this.openingTime = openingTime;
}
public void setClosingTime(int closingTime) {
this.closingTime = closingTime;
}
public void setMappingURL(String mappingURL) {
this.mappingURL = mappingURL;
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
String url=request.getRequestURL().toString();
if(mappingURL==null || url.matches(mappingURL)){
Calendar c=Calendar.getInstance();
c.setTime(new Date());
int now=c.get(Calendar.HOUR_OF_DAY);
if(now<openingTime || now>closingTime){
request.setAttribute("msg", "注册开放时间:9:00-12:00");
request.getRequestDispatcher("/msg.jsp").forward(request, response);
return false;
}
return true;
}
return true;
}
}
过滤器:
//过滤器控制编码
public class EnCodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter)
throws IOException, ServletException {
HttpServletRequest request1 = (HttpServletRequest)request;
HttpServletResponse response1 = (HttpServletResponse)response;
//预处理
request1.setCharacterEncoding("UTF-8");
response1.setCharacterEncoding("UTF-8");
//
filter.doFilter(request, response);
}
public void init(FilterConfig arg0) throws ServletException {
}
}
Aspect:
@Component("annotationTest")
@Aspect
public class AnnotationTest {
//定义切点
@Pointcut("execution(* *.saying(..))")
public void sayings(){}
@Before("sayings()")
public void sayHello(){
System.out.println("注解类型前置通知");
}
//后置通知
@After("sayings()")
public void sayGoodbey(){
System.out.println("注解类型后置通知");
}
//环绕通知。注意要有ProceedingJoinPoint参数传入。
@Around("sayings()")
public void sayAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("注解类型环绕通知..环绕前");
pjp.proceed();//执行方法
System.out.println("注解类型环绕通知..环绕后");
}
}
总结:
三者功能类似,但各有优势,从过滤器--》拦截器--》切面,拦截规则越来越细致,执行顺序依次是过滤器、拦截器、切面。一般情况下数据被过滤的时机越早对服务的性能影响越小,因此我们在编写相对比较公用的代码时,优先考虑过滤器,然后是拦截器,最后是aop。比如权限校验,一般情况下,所有的请求都需要做登陆校验,此时就应该使用过滤器在最顶层做校验;日志记录,一般日志只会针对部分逻辑做日志记录,而且牵扯到业务逻辑完成前后的日志记录,因此使用过滤器不能细致地划分模块,此时应该考虑拦截器,然而拦截器也是依据URL做规则匹配,因此相对来说不够细致,因此我们会考虑到使用AOP实现,AOP可以针对代码的方法级别做拦截,很适合日志功能。参考博文:
https://blog.csdn.net/dingfei136913/article/details/78840672
https://blog.csdn.net/Jintao_Ma/article/details/52972482