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">测 ...
随机推荐
- C++程序设计教材目录思维导图(增C++Primer)
正在做C++思维导图,大工程,比较艰苦. 先做了三个C++教材目录的思维导图.C++教材不等于C++,这个容易些.看思维导图,整理所学知识,这个可以会. 给出三张图,对应三种教材: 谭浩强. C++程 ...
- Web.xml详解(转)(Filter,context,listener)
web.xml 详细解释!!(链接) web.xml加载过程(步骤) 首先简单说一下,web.xml的加载过程. 当我们去启动一个WEB项目时,容器包括(JBoss.Tomcat等)首先会读取项目we ...
- java memory allocation(转)
Java的运行时数据存储机制 Java程序在运行时需要为一系列的值或者对象分配内存,这些值都存在什么地方?用什么样的数据结构存储?这些数据结构有什么特点?本文试图说明此命题的皮毛之皮毛. 概念 对 ...
- linux 下安装编译配置 QT
注: 1,自己 make qt-everywhere-opensource-src s时,在./configure前主动装好以下3个 sudo apt-get install libX11-dev l ...
- jdbc框架-dbutils的简单使用
jdbc框架-dbutils的简单使用 dbutils:是apache组织的一个工具类,jdbc的框架,更方便我们使用 使用步骤: 1.导入jar包(commons-dbutils-1.4.jar) ...
- Request.Cookies和Response.Cookies
Request.Cookies创建的Cookie只能用于后台不能用于HTML的前台Response.Cookies操作过的Cookie,所有方法获取到的都是被更新过的值,也就是说Response.Co ...
- JavaScript —— 常用数据类型隐式转换
公用方法: let checkType = (data) => { if(data){ console.log(true); }else{ console.log(false); } } 一.字 ...
- shell学习(18)- split切分文件命令
Linux split命令用于将一个文件分割成数个. 该指令将大文件分割成较小的文件,在默认情况下将按照每1000行切割成一个小文件. 语法: split [--help][--version][-& ...
- GIT 用RSA加密方式来记住密码
ssh-kegen -t rsa -C "你的帐号";//生成rsa的公钥和密钥 当然这个要在GNU环境下来执行,要是用Windows的CMD是不可以的(不输入DIR时),感觉是因 ...
- qt开发ROS遇到这个问题 find_package(catkin) failed. catkin was neither found in the workspace nor in the CMAKE_PREFIX_PATH...
为了实现用Qt开发ROS界面开发环境,我几乎参阅了网上所有的配置教程,安装了多个版本的qt,在ubuntu14.04和ubuntu16.04上分别进行了配置,最后都成功了.不得不说的是用QTCREAT ...