前言
今天来把 SpringMVC 最后一部分学完,虽然课时很短,但是学起来还是很慢的,不过确收获很大。不得不感慨学大数据确实有必要把 SSM、SpringBoot 等各种 JavaEE 技术好好学一学,收获很大,尽管我们到现在 Java 代码写了应该20 W 行差不多,但是关于框架的代码我写的还是比较少的,主要是通过算法和 JavaFX 来练习 Java 基础语法。
1、拦截器
1.1、概念
- 拦截器是一种动态拦截方法调用的机制,在 SpringMVC 中动态拦截控制器方法的执行
- 作用
- 在指定方法调用前后执行预先设定的代码
- 阻止原始方法的执行(权限判断)
拦截器和过滤器的区别
- 归属不同:Filter 属于 Servlet 技术,Interceptor 属于 SpringMVC 技术
- 拦截内容不同:Filter 对所有访问进行增强,Interceptor 仅针对 SpringMVC 的访问进行增强
1.2、入门案例
1.2.1、拦截器功能类
在 controller 包下新建一个包 interceptor ,毕竟拦截器是 SpringMVC 的内容,创建自定义拦截器类(记得添加注解 @Component 让 SpringMVC 来对它进行管理)只需要实现 HandlerInterceptor 接口:
@Component public class ProjectInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); 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"); } }
这里需要注意的是 preHandle 方法的返回值,如果返回值为 false 代表直接截断请求,所以后面的控制器方法不会被执行,后面的 postHandle 和 afterCompletion 方法也不会执行。
如果返回 true 则代表不拦截,所以它会执行控制器方法以及 postHandle 和 afterCompletion 方法。
在之前的 SpringMvcSupport 类中添加拦截器,需要配置两个参数:拦截器对象(因为拦截器已经被 SpringMVC 所管理了,所以我们直接通过自动装配获得),拦截哪个请求。
访问 localhost:81/books:
上面我们配置的拦截器只能拦截 /books 这一个请求,如果我们想要拦截 /books/2 后者 /books/100 呢,只需要添加一个参数 "/books/*" 即可:
@Override protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*"); }
1.2.2、简化开发
现在我们的 SpringMvcConfig 是空的,只是带有一堆注解,我们可以把 SpringMvcSupport 和 SpringMvcConfig 进行合并,其实就是让 SpringMvcConfig 实现 WebMvcConfigurer 接口(和 SpringMvcSupport 很像,后者实现的是 WebMvcConfigurationSupport 接口):
@Configuration @ComponentScan("com.lyh.controller") @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer { @Autowired private ProjectInterceptor projectInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*"); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
因为我们已经把 SpringMvcSupport 和 SpringMvcConfig 合并了,所以现在我们就不需要让 SpringMvcConfig 扫描 "com.lyh.config" 包了 。
有无拦截器的区别
可以看到,单个拦截器的时候,如果在 preHandle 方法返回 false,将直接终止执行(afterCompletion 方法也不会执行,但是这种情况在多个拦截器的情况下有所不同)。
1.3、拦截器参数
我们说拦截器可以帮我们做一些判断,来决定是否允许这个请求进一步进入 controller 方法,那它必然有一些上下文参数
1.3.1、preHandle 方法
- HttpServletRequest request
- HttpServletResponse response,
- Object handler
对于 request 和 response 这两个对象,我们很容易想到可以拿它们来干什么,比如 request.getHeader("Content-type") 来获得请求头中的 Content-type 属性值。
而这里的 handler 其实是 HandlerMethod 类,它对反射技术中的 Method 对象进行了封装。我们可以通过它来获得原始执行的方法,具体可以干什么以后用到再学。
1.3.2、postHandle 方法
postHandle 方法没有 handler 对象,但是它有一个 ModelAndView 对象,如果我们页面跳转的话,可以通过它来设置一些属性,但是我们到目前为止都是返回 json 并不返回页面,所以这里不做学习。
1.3.3、afterCompletion 方法
afterCompletion 方法同样除了 request 和 response 对象之外,它有一个 Exception 类型的对象,也就是说如果我们的表现层抛出异常的话,我们可以在这里拿到它,但是我们上一篇已经用了 SpringMVC 统一的异常处理器,所以这里也没啥用。
总之,这三个方法实用性最强的就是 preHandle 方法,它可以决定是否放行请求的下一步执行。
1.4、拦截器链配置
- 当配置多个拦截器时,形成拦截器链
- 拦截器的拦截顺序取决于我们 addIterceptors 方法中注册拦截器的顺序
1.4.2、创建拦截器类
我们在 interceptor 包下再创建一个拦截器类:
@Component public class ProjectInterceptor2 implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle2"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle2");; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion2"); } }
配置拦截器:
访问 localhost:85/books
可以看到,拦截器的拦截顺序取决于我们 addIterceptors 方法中注册拦截器的顺序: pre1 -> pre2 -> controller -> post2 -> post1 -> after2 -> after1。
注意:多个的拦截器情况下,比如下图:
- 如果 pre3 返回 flase(pre1 和 pre2都返回 true),那么程序并不是直接终止,而是依然会执行 after2 和 after1;
- 如果 pre2 返回 flase(pre1返回 true),那么程序并不是直接终止,而是依然会执行 after1;
- 如果 pre1 返回 flase,那么程序直接终止。
总结
至此,SpringMVC 的学习结束,SSM 现在已经是一个程序员必知必会的东西了,可以说是一个应届生校招的基本技能,而不是亮点。接下来再学学 SpringBoot ,这是我们大数据开发中正儿八经会用到的东西,完了做个项目巩固巩固。