java拦截器Interceptor

1,526 阅读5分钟

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实现

/**
 * @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类就具体在这里配置)
@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,则关闭浏览器就会失效
    }
}
/**
 * @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方法");
    }
}
@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()方法,方法内容为拦截到请求后的处理