Springboot笔记<7>过滤器与拦截器

过滤器 拦截器

过滤器

过滤器拦截的是URL

Spring中自定义过滤器(Filter)一般只有一个方法,返回值是void,当请求到达web容器时,会探测当前请求地址是否配置有过滤器,有则调用该过滤器的方法(可能会有多个过滤器),然后才调用真实的业务逻辑,至此过滤器任务完成。过滤器并没有定义业务逻辑执行前、后等,仅仅是请求到达就执行。

特别注意:过滤器方法的入参有request,response,FilterChain,其中FilterChain是过滤器链,使用比较简单,而request,response则关联到请求流程,因此可以对请求参数做过滤和修改,同时FilterChain过滤链执行完,并且完成业务流程后,会返回到过滤器,此时也可以对请求的返回数据做处理。

两种方法使用过滤器,注入bean到ioc容器或者使用@WebServlet注解。

「注意」:@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们还需在配置类中加另外一个注解:@ServletComponetScan,指定扫描的包。定义过滤器类:HelloFilter,在过滤器类上添加@WebFilter注解配置过滤器名称、过滤路径等属性,通过@Order配置过滤器执行顺序。注意使用@WebServlet、@WebFilter、@WebListener等servlet注解时需要在springboot的启动类上添加@ServletComponentScan注解,否则不会生效。@Order值越小优先级越高。

//第一种,注册bean
@Order(value = 2)
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        System.out.println("myFilter提示:服务启动..........");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("myFilter提示:调用过滤器Filter的doFilter()方法..........");
        System.out.println("ip地址为"+servletRequest.getRemoteAddr());
        //放行通过
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

        System.out.println("myFilter提示:服务关闭..........");
    }
}




@Configuration
public class MyFilterConfig {
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        MyFilter filter = new MyFilter();
        registrationBean.setFilter(filter);
        //设置过滤器拦截请求
        List<String> urls = new ArrayList<>();
        urls.add("/*");
        registrationBean.setUrlPatterns(urls);
        return registrationBean;
    }
}
//第二种,基于注解
@WebFilter(urlPatterns="/*", filterName = "myFilterByAnnotation")
@Order(value = 3)
public class MyFilterByAnnotation implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("myFilterByAnnotation提示:服务启动..........");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("myFilterByAnnotation提示:调用过滤器Filter的doFilter()方法..........");
        System.out.println("ip地址为"+servletRequest.getRemoteAddr());
        //放行通过
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

        System.out.println("myFilterByAnnotation提示:服务关闭..........");
    }
}

@SpringBootApplication
@ServletComponentScan
@Import({Teacher.class})
public class SpringbootReviewApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootReviewApplication.class, args);
    }

}

//myFilter提示:调用过滤器Filter的doFilter()方法..........
//myFilterByAnnotation提示:调用过滤器Filter的doFilter()方法..........

拦截器

拦截器拦截的是URL,拦截器有三个方法,相对于过滤器更加细致,有被拦截逻辑执行前、后等。Spring中拦截器有三个方法:preHandle,postHandle,afterCompletion。分别表示如下:

  • public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o)表示被拦截的URL对应的方法执行前的自定义处理
  • public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView)表示此时还未将modelAndView进行渲染,被拦截的URL对应的方法执行后的自定义处理
  • public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e)表示此时modelAndView已被渲染,执行拦截器的自定义处理

拦截器的应用场景

1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page VIEW)等。
2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。

拦截器使用方法

1.在任务类增加注解 @Configuration 注册bean,继承 WebMvcConfigurerAdapter 代表配置拦截器的适配器

2.重写 addInterceptors 添加需要的拦截器地址及需要排除的拦截地址

SpringMVC 拦截器拦截 /* 和 /** 的区别:

  • /* : 匹配一级,即 /add , /query 等
  • /** : 匹配多级,即 /add , /add/user, /add/user/user… 等
@Slf4j
public class MyInterceptor implements HandlerInterceptor {

    long start;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        start = System.currentTimeMillis();
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor cost="+(System.currentTimeMillis()-start));
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}


/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  //所有请求都被拦截包括静态资源
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求
    }
}

拦截器原理

1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】

2、先来顺序执行 所有拦截器的 preHandle方法

  • 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
  • 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;

3、如果任何一个拦截器返回false。直接跳出不执行目标方法

4、所有拦截器都返回True。执行目标方法

5、倒序执行所有拦截器的postHandle方法。

6、前面的步骤有任何异常都会直接倒序触发 afterCompletion

7、页面成功渲染完成以后,也会倒序触发 afterCompletion

拦截器与过滤器的区别

1、拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
2、拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
3、拦截器只能对ACTION请求起作用,而过滤器则可以对几乎所有的请求起作用。
4、拦截器可以访问ACTION上下文、值栈里的对象,而过滤器不能访问。
5、在ACTION的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
6、拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。过滤器 拦截器 AOP区别

AOP(面向切面)面向切面拦截的是类的元数据(包、类、方法名、参数等)相对于拦截器更加细致,而且非常灵活,拦截器只能针对URL做拦截,而AOP针对具体的代码,能够实现更加复杂的业务逻辑。三者功能类似,但各有优势,从过滤器--》拦截器--》切面,拦截规则越来越细致,执行顺序依次是过滤器、拦截器、切面。一般情况下数据被过滤的时机越早对服务的性能影响越小,因此我们在编写相对比较公用的代码时,优先考虑过滤器,然后是拦截器,最后是aop。比如权限校验,一般情况下,所有的请求都需要做登陆校验,此时就应该使用过滤器在最顶层做校验;日志记录,一般日志只会针对部分逻辑做日志记录,而且牵扯到业务逻辑完成前后的日志记录,因此使用过滤器不能细致地划分模块,此时应该考虑拦截器,然而拦截器也是依据URL做规则匹配,因此相对来说不够细致,因此我们会考虑到使用AOP实现,AOP可以针对代码的方法级别做拦截,很适合日志功能。

posted @ 2021-12-21 14:30  aixueforever  阅读(384)  评论(0编辑  收藏  举报