在Spring Boot中,拦截器可以分为两种类型:

  • 一是WebMVC,负责拦截请求,类似于过滤器,对用户的请求在Controller接收前进行处理,在Controller处理完成后加工结果等。使用时需实现HandlerInterceptor接口。
  • 一是AOP,拦截指定类型的方法,通过动态代理模式实现,可以在方法的调用前和调用后添加功能处理。使用时需要实现MethodInterceptor接口。

拦截器(Interceptor)和过滤器(Filter)对比

相同点:

  • 都可以对请求进行提前处理和响应内容加工。
  • 都支持多个拦截器/过滤器的链路传递。

不同点:

  • 拦截器由Spring提供,过滤器由Servlet提供。

HandlerInterceptor

HandlerInterceptor接口属于顶级接口,里面一共有三个default方法(这是jdk8的新特性,用来在接口中编写带有方法体的方法。实现接口可以不重写default方法,默认调用的仍是接口中的default方法体)

public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
} default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
} default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
boolean preHandle:会在controller处理前调用该方法,方法返回true则进入对应的controller。方法返回false则不会进入controller。可以用来编码,安全控制,权限校验等。
void postHandle:在controller处理完成返回ModelAndView后执行。此时还没有进行视图渲染,还可以修改ModelAndView。
void afterCompletion:整个请求已经处理完成了,可能返回了正常的请求结果,也可能返回一个异常。

下面我们来做一个简单的demo。

先定义一个controller:

@RestController
public class UserController { @GetMapping("users/{id}")
public String getUser(@PathVariable("id") String id) {
System.out.println("controller[url=users/" + id + "]");
return "testUser";
} @GetMapping("users/login")
public String test(User user, Model model) {
System.out.println("controller[url=users/login]");
model.addAttribute("id", user.getId());
model.addAttribute("name", user.getName());
model.addAttribute("password", user.getPassword());
model.addAttribute("mail", user.getMail());
return "index";
}
}

里面提供了两个请求链接:/users/{id}和/users/login,这里为了简单方便,我们均将请求方式设置为get。

定义一个拦截器UserInteceptor(这里先测试第一个方法preHandle):

@Component
public class UserInteceptor implements HandlerInterceptor { @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle;");
Map<String, String[]> map = request.getParameterMap();
map.forEach((k, v) -> {
System.out.println("[Key=" + k + ";Value=" + StringUtils.join(v) + "];");
});
return true;
} @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");
}
}

然后将拦截器添加进容器中,设定它的拦截路径为/users/**:

@Configuration
public class InteceptorConfig implements WebMvcConfigurer { @Autowired
private UserInteceptor userInteceptor; @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInteceptor).addPathPatterns("/users/*");
}
}

就此一个简单的项目已经构成,我们启动程序,然后访问/users/login?id=1&username=yxf&password=123&mail=5@qq.com

可以看到访问结果:

preHandle; //----------------------------调用了preHandle方法
[Key=id;Value=]; //---------------------打印request中的id
[Key=name;Value=yxf]; //-----------------打印request中的name
[Key=password;Value=]; //-------------打印request中的password
[Key=mail;Value=@qq.com]; //------------打印request中的mail
controller[url=users/login] //-----------在preHandle之后,这里进入UserController的test方法。
postHandle //----------------------------controller处理完成后调用PostHandle方法。
//---------------在这中间其实还有dispatchServlet调用视图解析器对View的解析等------------------
afterCompletion //-----------------------postHandle处理完成后调用afterCompletion。

顺序是按照preHandle→Controller→postHandle→视图渲染器→afterCompletion的顺序执行。

MethodInterceptor

MethodInterceptor继承关系:

在MethodInterceptor接口中,只提供了一个方法

Object invoke(MethodInvocation invocation) throws Throwable;

首先分析方法的传入参数MethodInvocation

MethodInvocation对象继承关系如下

相关方法说明

Method getMethod(); //获取Java反射类Method

Object[] getArguments(); // 获取方法的传入参数

Object proceed() throws Throwable; // 继续执行方法

Object getThis(); // 获取方法所在的对象

AccessibleObject getStaticPart(); // 获取的也是Java反射类Method

测试Demo

为了方便一部分功能展示,我们这里定义一个注解用来后续操作

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DemoAnnotation { String value() default "test"; }

定义我们的方法过滤器,从参数对象获取到被拦截的方法的相关信息,这里为了简单直接将信息打印在控制台(实际情况因具体业务而异)。

public class DemoInterceptor implements MethodInterceptor {

    @Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("--------------------------------------------------"); // 参数
Object[] object = methodInvocation.getArguments();
for (Object obj : object) {
System.out.println(obj.getClass().getName());
if (obj instanceof String) {
System.out.println("param = " + ((String) obj).toString());
}
} // 对象
Object obj = methodInvocation.getThis();
System.out.println(obj.getClass().getName()); // 方法
Method method = methodInvocation.getMethod(); // 继续执行
obj = methodInvocation.proceed();
System.out.println(obj.getClass().getName()); // 获取注解
DemoAnnotation d = method.getAnnotation(DemoAnnotation.class);
if (d == null) {
System.out.println("当前类没有DemoAnnotation注解");
} else {
System.out.println(d.value());
} System.out.println("--------------------------------------------------");
return null;
}
}

配置过滤器。(方法过滤器实际可以算是实现AOP的一种方式,我们需要定义一个切面,配置它的切点和增强)

    @Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor() {
// 声明切点
// JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
// pointcut.setPatterns("com.example.*");
AspectJExpressionPointcut pointcut =new AspectJExpressionPointcut();
pointcut.setExpression("execution(* com.example..*(*))"); // 拦截com.example包和子包下带一个参数的任何方法 // 声明增强
DemoInterceptor interceptor = new DemoInterceptor(); // 配置切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(interceptor); return advisor;
}

定义调用类,这里为了简单,也没有写service层,直接在controller层调用了。

@RestController
public class DemoController { @GetMapping("/demo/{id}")
public String demo(@PathVariable("id") String id) {
return "test demo";
} @GetMapping("/demo2/{id}")
@DemoAnnotation
public String demo2(@PathVariable("id") String id) {
return "test demo";
} }

运行程序,分别访问上面的两个地址,打印如下

/demo/1

--------------------------------------------------
java.lang.String
param =
com.example.demo.controller.DemoController
java.lang.String
当前类没有DemoAnnotation注解
--------------------------------------------------

/demo2/2

--------------------------------------------------
java.lang.String
param =
com.example.demo.controller.DemoController
java.lang.String
test
--------------------------------------------------

结束

spring拦截器Interceptor的更多相关文章

  1. web 过滤器 Filter、 Spring 拦截器 interceptor

    1.过滤器(Filter)(在web.xml中注册过滤器) 首先说一下Filter的使用地方,我们在配置web.xml时,总会配置下面一段设置字符编码,不然会导致乱码问题: <filter> ...

  2. spring拦截器(interceptor)简介

    1. 拦截器用途 (1)拦截未登录用户直接访问某些链接 (2)拦截日志信息 (3)拦截非法攻击,比如sql注入 2. 涉及jar.类 (1)spring-webmvc.jar (2)HandlerIn ...

  3. Spring中过滤器(Filter)和拦截器(Interceptor)的区别和联系

    在我们日常的开发中,我们经常会用到Filter和Interceptor.有时同一个功能.Filter可以做,Interceptor也可以做.有时就需要考虑使用哪一个比较好.这篇文章主要介绍一下,二者的 ...

  4. Spring拦截器中通过request获取到该请求对应Controller中的method对象

    背景:项目使用Spring 3.1.0.RELEASE,从dao到Controller层全部是基于注解配置.我的需求是想在自定义的Spring拦截器中通过request获取到该请求对应于Control ...

  5. java之拦截器Interceptor

    1,拦截器的概念    java里的拦截器是动态拦截Action调用的对象,它提供了一种机制可以使开发者在一个Action执行的前后执行一段代码,也可以在一个Action执行前阻止其执行,同时也提供了 ...

  6. 过滤器(Filter)和拦截器(Interceptor)

    过滤器(Filter) Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序.它依赖于servlet容器,在实现上,基于函数回调,它可以对几乎所有请求 ...

  7. spring拦截器中修改响应消息头

    问题描述 前后端分离的项目,前端使用Vue,后端使用Spring MVC. 显然,需要解决浏览器跨域访问数据限制的问题,在此使用CROS协议解决. 由于该项目我在中期加入的,主要负责集成shiro框架 ...

  8. Spring 拦截器实现+后台原理(HandlerInterceptor)

    过滤器跟拦截器的区别 spring mvc的拦截器是只拦截controller而不拦截jsp,html 页面文件的.这就用到过滤器filter了,filter是在servlet前执行的,你也可以理解成 ...

  9. 二十五、过滤器Filter,监听器Listener,拦截器Interceptor的区别

    1.Servlet:运行在服务器上可以动态生成web页面.servlet的声明周期从被装入到web服务器内存,到服务器关闭结束.一般启动web服务器时会加载servelt的实例进行装入,然后初始化工作 ...

随机推荐

  1. ElasticSearch入门介绍之会当凌绝顶(一)

    ElasticSearch也是一款非常优秀的开源的全文检索框架,以大名鼎鼎的Apache Lucene为基础,高度封装了更丰富,易用的API,同时与Apache Solr一样,提供了非常强大的分布式集 ...

  2. .net面试问题总结

    原文://http://blog.csdn.net/wenyan07/article/details/41541489 用.net做B/S结构的系统,您是用几层结构来开发,每一层之间的关系以及为什么要 ...

  3. HZOI20190803 B题

    题目:https://www.cnblogs.com/Juve/articles/11295333.html 话说这题方法挺多 40分:暴力 65:莫队,你会T得飞起 我考场上没打出带修莫队,没有修改 ...

  4. Ionic 列表、文本 自动 换行

    1.采用row 布局的row-warp 来处理 <div class="item item-icon-right"> <span>图片相册</span ...

  5. SpringMVC配置顺序的问题

    1:web.xml:web应用一经加载,先来找他         1):指明applicationContext的位置         2):引入spring监听,ContextLoaderListe ...

  6. MyBatis配置文件(二)--settings配置

    settings是MyBatis中最复杂的配置,它能影响MyBatis底层的运行,大部分情况下使用默认值,只需要修改一些常用的规则即可.常用规则有自动映射.驼峰命名映射.级联规则.是否启动缓存.执行器 ...

  7. [转载] OpenCV2.4.3 CheatSheet学习(三)

    四.图像处理(呵呵,重头戏来了) 1. 滤波 filter2D() 用核函数对图像做卷积. sepFilter2D() 用分解的核函数对图像做卷积. 首先,图像的每一行与一维的核kernelX做卷积: ...

  8. 解Bug之路-记一次中间件导致的慢SQL排查过程

    解Bug之路-记一次中间件导致的慢SQL排查过程 前言 最近发现线上出现一个奇葩的问题,这问题让笔者定位了好长时间,期间排查问题的过程还是挺有意思的,正好博客也好久不更新了,就以此为素材写出了本篇文章 ...

  9. sqoop的数据抽取过程记录

    今天公司抽取了4千万的表大概十几G 用sqoop抽取是30--40分钟 开了两个map.模型是oracle----hdfs(hive).以前只抽过几十万级别,所以千万级别感觉还是spilt做好切分和定 ...

  10. java代理概念

    代理的概念 动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的. 动态代理技术就是用来产生一个对象的代理对象的 ...