Java拦截器(Inteerceptor)

1.什么是拦截器

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

作用域:动态拦截Action调用的对象(Controller)

原理:大部分的时候,拦截器方法都是通过代理的方式来调用的。

  • 使用AOP切面功能来实现

Struts2的拦截器实现相对简单。当请求到达Struts的ServletDispatcher时,会查找配置文件,并根据配置实例化相对的拦截器对象,然后串成一个列表(List),最后一个一个的调用列表中的拦截器。Struts2的拦截器是可插拔的,拦截器是AOP的一个实现。Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或者字段时,Struts2拦截器链中的拦截器就会按照之前定义的顺序进行调用。

  • 使用Spring的拦截器相关接口来自定义拦截器
    • 实现WebMvcConfigurer接口,重写addCorsMappings()方法和addInterceptors()方法【配置拦截器】
    • 实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter,重写preHandle()方法【自定义拦截器】

2.代码实现

Aop实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @author yhwang
* @date 2021年06月28日 18:37
* 切面方式配置的话,得配置到包路径下或者每个具体方法都要配置,个人不是很喜欢用切面的方式来配置拦截器,用来记录日志或者其他功能可以使用aop。
*/
@Aspect
@Component //将切面解析为Bean 把它当做一个组件交给Spring 管理。
class TestAspect {

@Pointcut("execution(* com.springboot.dev.db01.controller.*.*(..))") // 切点定义到包下所有类所有方法
public void testControllerAspect(){//切点隐射,命名不规范
//PS:定义切点方法内的代码好像不会执行,这里不用谢东西
}

/**
* 在本类定义的切点之前做的事情
* 一般都是判断session内有没有用户信息,没有用户信息重定向登录页面
*/
@Before("testControllerAspect()")
public void beforeAspect(){
System.out.println("切点方法之前做的事情");
}

/**
*
*/
@After("testControllerAspect()")
public void afterAspect(){
System.out.println("切点方法之后做的事情");
}
}

实现WebMvcConfigurer接口

WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter

基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口。在Spring Boot 1.5版本都是靠重写WebMvcConfigurerAdapter的方法来添加自定义拦截器,消息转换器等。SpringBoot 2.0 后,该类被标记为@Deprecated(弃用)。官方推荐直接实现WebMvcConfigurer或者直接继承WebMvcConfigurationSupport。

 简单介绍一下WebMvcConfigurer中比较重要的几个方法:

  • addInterceptors添加拦截器
  • addCorsMappings跨域
  • addViewControllers页面跳转(不用像现在我们要写一个Controller进行映射就可实现跳转)
  • addResourceHandlers静态资源(自定义静态资源映射目录)
  • configureDefaultServletHandling默认静态资源处理器
  • configureViewResolvers视图解析器(配置请求视图映射,配置了以后我们返回一个页面路径的字符串时,解析器会帮我们拼装前缀和后缀等信息)
  • configureMessageConverters信息转换器(比如我们入参的信息直接转换成json或者转换成对应的bean类就具体在这里配置)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Configuration //标识此为一个配置类
public class MyWebConfigurer implements WebMvcConfigurer {

/**
* 重写addInterceptors()实现拦截器
* 配置:要拦截的路径以及不拦截的路径
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
InterceptorRegistration registration = registry. addInterceptor (new LoginInterceptor()) ;
//addPathPatterns() 方法 添加需要拦截的路径
registration.addPathPatterns("/**");
//addPathPatterns() 方法 添加不需要拦截的路径
registration.excludePathPatterns(
"/**/*.html",
"/**/*.js"
);
}

/**
* 重写addCorsMapping()解决跨域问题
* 配置:允许http请求进行跨域访问
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")//指哪些接口url需要藏家跨域设置
.allowedOrigins("*")//指的是前端哪些页面允许被跨域
.allowCredentials(true)//需要嗲cookie等凭证,设置为true,就会把cookie的相关信息带上
.allowedMethods("GET","POST","PUT","DELETE","OPTIONS","HEAD")//指的是允许哪些方法
.maxAge(3600);//cookie的失效时间,单位为秒(s),若设置为-1,则关闭浏览器就会失效
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* @author yhwang
* 自定义拦截器 判断用户是否登录
* @date 2021年06月28日 19:03
*/
public class LoginInterceptor extends HandlerInterceptorAdapter {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("执行了Interceptor的preHandle方法");
try {
//统一拦截(查询当前session是否存在Usser用户信息)(这里User会在每次登陆成功后,写入session)
User user = (User) request.getSession().getAttribute("User");
if (user != null) {
return true;
}
//这里设置拦截以后重定向的页面,一般设置为登陆页面地址
response.sendRedirect(request.getContextPath() + "/demo/loginPage");
} catch (IOException e) {
e.printStackTrace();
}
return true;//如果设置为false时,被请求时,拦截器执行到此处将不会继续操作
//如果设置为true时,请求将会继续执行后面的操作
}


/***
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("执行了拦截器的postHandle方法");
}

/***
* 整个请求结束之后被调用,也就是在DispatchServlet渲染了对应的视图之后执行(主要用于进行资源清理工作)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("执行了拦截器的afterCompletion方法");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@RequestMapping(value = {"", "/", "/index"}, method = RequestMethod.GET)
public String index(Model model, HttpServletRequest request) {
User user = (User) request.getSession().getAttribute("user");
model.addAttribute("user", user);
return "users/index";
}

@RequestMapping(value = {"/login"}, method = RequestMethod.GET)
public String loginIndex() {
return "users/login";
}

@RequestMapping(value = {"/login"}, method = RequestMethod.POST)
public String login(@RequestParam(name = "username")String username, @RequestParam(name = "password")String password,
Model model, HttpServletRequest request) {
User user = userService.getPwdByUsername(username);
String pwd = user.getPassword();
String password1 = MD5Utils.md5Code(password).toUpperCase();
String password2 = MD5Utils.md5Code(password1).toUpperCase();
if (pwd.equals(password2)) {
model.addAttribute("user", user);
request.getSession().setAttribute("user", user);
return "redirect:/index";
} else {
return "users/failed";
}
}

小结:从上面的代码可以看出,要实现拦截器一般需要以下几个步骤:

  • 1、写一个实现了WebMvcConfigurer接口的配置类
  • 2、重写其中的addCorsMappings()方法【配置跨域信息】和addInterceptors()方法【配置拦截器信息,如拦截路径和开放路径等】
  • 3、写一个实现HandlerInterceptor接口的自定义拦截器
  • 4、重写其中的preHandle()方法,方法内容为拦截到请求后的处理