Filter、Intercepter、AOP的区别
在使用Spring MVC开发RESTful API的时候,我们经常会使用Java的拦截机制来处理请求,Filter是Java本身自带拦过滤器,Interceptor则是Spring自带的拦截器,而Aspect切面是Spring AOP一个概念,主要的使用场景有:日志记录、事务控制和异常处理,该篇文章主要说说它们是如何实现的以及他们之间的差别,在这过程中也会探讨全局异常处理机制的原理以及异常处理过程。
Filter
我对Filter过滤器做了以下总结:
介绍:
java的过滤器,依赖于Sevlet,和框架无关的,是所有过滤组件中最外层的,从粒度来说是最大的,它主要是在过滤器中修改字符编码(CharacterEncodingFilter)、过滤掉没用的参数、简单的安全校验(比如登录不登录之类)
实现和配置方式
1.直接实现Filter接口+@Component
2.@Bean+@Configuration(第三方Filter)
3.web.xml配置方式
Filter的实现方式
@Componentpublic class TimeFilter implements Filter {@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化TimeFilter...");
}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {System.out.println("-------TimeFilter Start--------");long start = new Date().getTime();filterChain.doFilter(request, response);System.out.println("TimeFilter执行耗时:" + (new Date().getTime() - start));System.out.println("-------TimeFilter End--------");}@Overridepublic void destroy() {System.out.println("销毁TimeFilter...");}}
注意:关于filterChain.doFilter(request,response,filterChain),执行filterChain.doFilter的意思是将请求转发给过滤器链上的下一个对象,如果没有filter那就是你请求的资源。一般filter都是一个链,web.xml 里面配置了几个就有几个。一个一个的连在一起这里指的是下一个Filter, request->filter1->filter2->filter3->...->response。我们定义完Filter之后,如果我们不使用@Component注解注入,可以使用另一种方式将Filter注入到我们的容器中,这里使用@Bean的形式定义,通过自定义配置类WebConfig实现配置,最后返回registrationBean,这个方法主要有两个好处就是第一我们可以通过registrationBean.setUrlPatterns(urls)来指明filter在哪些路径下起作用,第二我们可以使用该方法去注入第三方的filter,原因的很多地方的filter其实并不是以@Component注入方式(也就是没有标注@Component注解),这时候我们就只能使用第二种方式来实现了。
@Configurationpublic class WebConfig extends WebMvcConfigurerAdapter {@AutowiredTimeInterceptor timeInterceptor;@Beanpublic FilterRegistrationBean charsetFilter(){FilterRegistrationBean registrationBean = new FilterRegistrationBean();TimeFilter timeFilter = new TimeFilter();CharsetFilter charsetFilter = new CharsetFilter();registrationBean.setFilter(charsetFilter);registrationBean.setFilter(timeFilter);//相当于@webFilter的@WebInitParam()注解的作用Map<String,String> paramMap = new HashMap<>();paramMap.put("charset","utf-8");registrationBean.setInitParameters(paramMap);//相当于@webFilter的 urlPatterns = "/*"的作用List<String> urls = new ArrayList<>();urls.add("/*");//urls.add("/user/*");registrationBean.setUrlPatterns(urls);return registrationBean;}
我们在controller中定义一个getInfo()方法:
//请求路径的{id}回传到方法里也就是传到(@PathVariable String id)的id里@RequestMapping(value = "/user/{id:\\d+}",method = RequestMethod.GET)@JsonView(User.UserDetailView.class)
//这里因为UserDetailView继承了UserSimpleView所有会返回username和password@ApiOperation("获取用户信息")public User getInfo(@PathVariable Integer id) {// throw new UserNotExistException(id);System.out.println("进入getInfo()服务");User user = new User();user.setId(1);user.setUsername("jacklin");user.setPassword("123");return user;}
当我们调用controller中的getInfo()方法的时候,看看请求响应是否成以及控制台的输出:
GET请求发送成功,返回200,控制台输出如下:
从上述结果,我们可以分析得出,当客户端发送请求,到达Controller方法之前,先执行Filter初始化操作,接着进入Controller的方法体,最后执行完成,通过分析我们明白了Filter的工作原理和方法的执行顺序!
Interceptor
我对Interceptor过滤器做了以下总结(导图中加粗部分是重点):
简介:
spring框架的拦截器,主要依赖于Spring MVC框架,它是在 service 或者一个方法调用前,调用一个方法,或者在方法调用后,调用一个方法。
实现和配置方式:
实现HandlerInterceptor接口看,并重写preHandle、postHandle、afterCompletion方法。
解释说明:
SpringMVC中的Interceptor是链式的调用的,在一个应用中或者是在一个请求中可以同时存在多个Interceptor,每个Inteceptor的调用都会按照它的声明顺序依次执行,而且最先执行的Intecptor的preHandler方法,所以可以在这个方法中进行一些前置初始化操作或者是堆当前请求的一个预处理,也可以在这个方法中进行一些判断是否要继续进行下去。
该方法的返回值是Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;
当返回值为true 时就会继续调用下一个Interceptor的preHandle方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller方法。
Interceptor拦截器的实现方式
/*** @Author 林必昭* @Date 2019/7/4 13:15*/@Componentpublic class TimeInterceptor implements HandlerInterceptor {/*** preHandle方法的返回值是boolean值,当返回的是false时候,不会进入controller里的方法*/
@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("------->preHandle");System.out.println("控制器类名:" + ((HandlerMethod) handler).getBean().getClass().getName());
//获取类名System.out.println("控制器中的对应的方法名:" + ((HandlerMethod) handler).getMethod().getName());
//获取类中方法名request.setAttribute("startTime", new Date().getTime());
return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("------->postHandle");Long start = (Long) request.getAttribute("startTime");System.out.println("TimeInterceptor 执行耗时:" + " " + (new Date().getTime() - start));}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception {System.out.println("------->afterCompletion");Long start = (Long) request.getAttribute("startTime");System.out.println("TimeInterceptor 执行耗时:" + " " + (new Date().getTime() - start));
System.out.println("Exception is " + e);}}
注意:我们使用@Component定义Interceptor之后,还不能起作用,好要进行下一步配置,我们在之前定义的WebConfig配置类继承抽象类WebMvcConfigurerAdapter,将Interceptor注入容器中:
@Configurationpublic class WebConfig extends WebMvcConfigurerAdapter {@AutowiredTimeInterceptor timeInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(timeInterceptor);}}
这样Interceptor就起作用了,同样,我们通过发送请求,观察控制台的输出,来分析结果:
从TimeInterceptor拦截器结果,我们可以分析得出,当客户端发送请求,到达Controller方法之前,先执行Interceptor的preHandler方法,接着进入Controller的方法体,通过Interceptor我们可以获取到对应的Controller和执行的方法名,接着执行postHandler方法,最后执行afterCompletion方法,如何结果出现异常,也会执行afterCompletion,这里没有异常,所以Exception为空。
那么当控制层中抛出异常,如果没有使用全局异常处理,在拦截器上也能捕获到异常信息,我们可以尝试一下,在Controller抛出一个RuntimeException,RuntimeException并没有在全局异常处理中被处理,Controller修改如下:
@RequestMapping(value = "/user/{id:\\d+}",method = RequestMethod.GET)@JsonView(User.UserDetailView.class)
//这里因为UserDetailView继承了UserSimpleView所有会返回username和password@ApiOperation("获取用户信息")public User getInfo(@PathVariable Integer id) {/*** 当抛出UserNotExistException异常的时候,会跳到ControllerExceptionHandler的handleUserNotExistException方法* 进行相应的处理*/throw new RuntimeException("user not exist!!");
//这里抛出一个RuntimeException// System.out.println("进入getInfo()服务");// User user = new User();// user.setId(1);// user.setUsername("jacklin");// user.setPassword("123");// return user;
}
观察控制台输出:
结果很明显了,当控制层出现异常的时候,异常没有被全局处理器处理,到达拦截器,拦截器会捕获到异常,这时候只执行了preHandle和afterCompletionn方法,并没有执行postHandle方法,控制台也输出了异常信息。
想想,如果抛出我们自定义异常,而且自定义异常被全局处理器拦截处理,异常还会到达我们的拦截器吗,我们来自定义一个异常UserNotExistException,如下:
public class UserNotExistException extends RuntimeException {private static final long serialVersionUID = -9136501205369741760L;private String id;public UserNotExistException(String id){super("user is not exist...");this.id = id;}public String getId() {
return id;}public void setId(String id) {
this.id = id;}}
接着,定义全局异常处理器GlobalExceptionHandler,使用@ControllerAdvice修饰:
/*** 全局异常处理,负责处理controller抛出的异常** @Author 林必昭* @Date 2019/7/4 11:31*/@ControllerAdvicepublic class GlobalExceptionHandler {@ExceptionHandler(UserNotExistException.class)@ResponseBody@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//服务器内部错误public Map<String, Object> handleUserNotExistException(UserNotExistException ex) {Map<String, Object> resultMap = new HashMap<>();resultMap.put("id", ex.getId());resultMap.put("message", ex.getMessage());return resultMap;
}}
然后,我们再在UserController中抛出我们的自定义异常UserNotExistException,观察控制台的输出,来分析结果:
public User getInfo(@PathVariable Integer id) {/*** 当抛出UserNotExistException异常的时候,会跳到ControllerExceptionHandler的handleUserNotExistException方法* 进行相应的处理*///throw new RuntimeException("user not exist!!");throw new UserNotExistException("user not exist!!")}
从结果看出,异常时空的,证明我们定义的异常处理器已经生效,UserNotExistException在GlobalExceptionHandler已经被处理了,所有异常没有到达我们的拦截器,到这里我们可以得出异常的处理顺相顺序结论了,在文化在那个末尾会给出。
Aspect
我对Aspect过滤器做了以下总结:
在使用Spring AOP切面前,我们需要导入pom依赖:
<!-- 切面 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
切面拦截的实现方式
@Aspect@Componentpublic class TimeAspect {/*** 切入点*/@Around("execution(* com.lbz.web.controller.UserController.*(..))")
//UserController下的任何方法被调用都会执行这个切片public Object handleControllerMethod(ProceedingJoinPoint point) throws Throwable {System.out.println("TimeAspect start");long start = new Date().getTime();Object object = point.proceed();
//proceed中文意思是继续的意思,也就是切入,相当于filterChain.doFilter()Object[] args = point.getArgs();
//与Filter和Interceptor的区别是,可以获取到UserController里方法的参数for (Object arg : args) {System.out.println("控制层的方法对应参数是:" + arg);}
System.out.println("TimeAspect执行耗时:" + (new Date().getTime() - start));System.out.println("TimeAspect end");return object;}}
这里的point.proceed()是继续的意思,也就是切入,相当于filterChain.doFilter(),与Filter和Interceptor不同的是,我们可以通过point.getArgs();拿到对应方法的参数,我们通过遍历把参数打印看一下。
从结果看出,我们可以看到我们拿到方法对应的参数,为1,也就是我们请求:http://localhost:8060/user/1 传入的id的值;
总结:
1.过滤器可以拿到原始方法的Http的请求和响应信息,拿不到对应方法的详细信息,拦截器既可以拿到原始方法的Http请求和响应信息,也能拿到对应方法的详细信息,但是拿不到被调用方法对应参数的值,而切面可以拿到被调用方法传递过来参数的值,但却拿不到原始的Http请求和响应对象。2.Controller方法抛出异常之后,最先捕获到异常的是切片,如果你定义了全局异常处理器并声明了ControllerAdvice,切片捕获到异常往外抛,就轮到全局异常处理器处理,接着到拦截器,再到过滤器,也就是:拦截作用顺序:Aspect->全局处理器->拦截器->过滤器->Tomcat
Filter、Intercepter、AOP的区别的更多相关文章
- spring boot: filter/interceptor/aop在获取request/method参数上的区别(spring boot 2.3.1)
一,filter/interceptor/aop在获取参数上有什么区别? 1,filter可以修改HttpServletRequest的参数(doFilter方法的功能), interceptor/a ...
- Filter ,Interceptor,AOP
一.Filter: Filter也称之为过滤器,它是Servlet技术中比较激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态 ...
- 过滤器和拦截器filter和Interceptor的区别
1.创建一个Filter过滤器只需两个步骤 创建Filter处理类 web.xml文件中配置Filter 2.Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的 ...
- Filter与Servlet的区别与联系
Filter与Servlet的区别与联系 转自 http://blog.csdn.net/gaibian0823/article/details/51027495 在我们写代码时,在web.xml中总 ...
- filter和interceptor的区别
前言 最近在面试的时候,被问到了这个问题,觉得答得不是很好,在此进行整理和记录,供自己学习,也希望能帮助到大家. 什么是Filter 在java的javax.servlet下有一个接口Filter.任 ...
- every();some();filter();map();forEach()各自区别:
every();some();filter();map();forEach()各自区别: (1)every()方法:(返回值为boolean类型) 对数组每一项都执行测试函数,知道获得对指定的函数返回 ...
- filter listener interceptor的区别
转自: http://www.cnblogs.com/shangxiaofei/p/5328377.html https://www.cnblogs.com/jinb/p/6915351.html 一 ...
- java---servlet与filter的联系与区别
filter是一个可以复用的代码片段,可以用来转换HTTP请求.响应和头信息.Filter不像Servlet,它不能产生一个请求或者响应,它只是修改对某一资源的请求,或者修改从某一的响应. 最近使用插 ...
- 【jQuery】【转】jQuery中filter()和find()的区别
Precondition: 现在有一个页面,里面HTML代码为: <div class="css"> <p class="rain">测 ...
随机推荐
- day13 memcache,redis上篇
memcache memcache简介 Memcached是一个自由开源的,高性能,分布式内存对象缓存系统. Memcached是以LiveJournal旗下Danga Interactive公司的B ...
- day39-Spring 03-JDK的动态代理
package cn.itcast.spring3; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Meth ...
- 跟我一起认识axure(一)
第一步下载:https://www.axure.com.cn/ 第二步点击安装,一路next 第三步:认识Axure RP工作界面
- JavaScript--微博发布效果
效果图: 实现思路: 当发布按钮被点击时,又会分为三种情况 1.如果输入的内容为空,弹出提示:不能发布空微博 2.如果输入的文字超过120,弹出提示,微博内容不能超过120 3.正常发布微博到列表里 ...
- hdu5442 Favorite Donut 后缀数组 长春网赛
wa从一点到晚上11点没停过,也不知道为什么错,第二天换了个思路做,终于过了.这题还是有点问题的,数据有点水,我看到有人贴的代码baabbaab这组数据是4 0,明显错的,但是却可以过. 下面的是我第 ...
- Run As none applicable
详解如何在myeclipse中运行JSP,Run As none applicable(图) 内容提要:对JSP的访问都是用浏览器进行的,没有Run on Server这个选项. 在MyEclip ...
- 2018-8-10-win10-uwp-如何创建修改保存位图
title author date CreateTime categories win10 uwp 如何创建修改保存位图 lindexi 2018-08-10 19:16:50 +0800 2018- ...
- [Java]ssh网上商城总结 标签: hibernatessh 2016-05-15 21:03 1099人阅读 评论(32)
前几日敲完了ssh网上商城,虽然现在已经敲完了整个系统,却发现自己对于ssh,了解的一点都不多,什么是struts2,什么是spring,什么是hibernate,自己都是稀里糊涂,然后看了一下后面的 ...
- js错误处理Try-catch和throw
1.try-catch语句 Try{ //可能会导致错误的代码 }catch(error){ //在错误发生时怎么处理 } 例如: try{ window.someNonexistentFunct ...
- 关于IOS 微信浏览器 点击输入框自动放大问题
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0& ...