详解java中spring里的三大拦截器

Filter

新建 TimeFilter

@Component
public class TimeFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    System.out.println("time filter init");
  }

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    System.out.println("time filter start");
    long startTime = System.currentTimeMillis();

    filterChain.doFilter(servletRequest, servletResponse);

    long endTime = System.currentTimeMillis();
    System.out.println("time filter consume " + (endTime - startTime) + " ms");
    System.out.println("time filter end");
  }

  @Override
  public void destroy() {
    System.out.println("time filter init");
  }
}

启动服务器,在浏览器输入:http://localhost:8080/hello?name=tom

可以在控制台输出如下结果:

time filter start
name: tom
time filter consume 3 ms
time filter end

可以看到,filter 先执行,再到真正执行 HelloController.sayHello() 方法。
通过 TimeFilter.doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 方法的参数可以看出,我们只能得到原始的 request 和 response对象,不能得到这个请求被哪个 Controller 以及哪个方法处理了,使用Interceptor 就可以获得这些信息。

Interceptor

新建 TimeInterceptor

@Component
public class TimeInterceptor extends HandlerInterceptorAdapter {

  private final NamedThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<>("startTimeThreadLocal");

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("time interceptor preHandle");

    HandlerMethod handlerMethod = (HandlerMethod) handler;
    // 获取处理当前请求的 handler 信息
    System.out.println("handler 类:" + handlerMethod.getBeanType().getName());
    System.out.println("handler 方法:" + handlerMethod.getMethod().getName());

    MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
    for (MethodParameter methodParameter : methodParameters) {
      String parameterName = methodParameter.getParameterName();
      // 只能获取参数的名称,不能获取到参数的值
      //System.out.println("parameterName: " + parameterName);
    }

    // 把当前时间放入 threadLocal
    startTimeThreadLocal.set(System.currentTimeMillis());

    return true;
  }

  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("time interceptor postHandle");
  }

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

    // 从 threadLocal 取出刚才存入的 startTime
    Long startTime = startTimeThreadLocal.get();
    long endTime = System.currentTimeMillis();

    System.out.println("time interceptor consume " + (endTime - startTime) + " ms");

    System.out.println("time interceptor afterCompletion");
  }
}

注册 TimeInterceptor

把 TimeInterceptor 注入 spring 容器

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

  @Autowired
  private TimeInterceptor timeInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(timeInterceptor);
  }
}

启动服务器,在浏览器输入:http://localhost:8080/hello?name=tom

可以在控制台输出如下结果:

time filter start
time interceptor preHandle
handler 类:com.nextyu.demo.web.controller.HelloController
handler 方法:sayHello
name: tom
time interceptor postHandle
time interceptor consume 40 ms
time interceptor afterCompletion
time filter consume 51 ms
time filter end

可以看到,filter 先于 interceptor 执行,再到真正执行 HelloController.sayHello() 方法。通过 interceptor 方法上的 handler 参数,我们就可以得到这个请求被哪个 Controller 以及哪个方法处理了。但是不能直接获取到这个方法上的参数值(在这里就是 HelloController.sayHello(String name) 方法参数 name 的值),通过 Aspect 就可以获取到。

Aspcet

新建 TimeAspect

@Aspect
@Component
public class TimeAspect {

  @Around("execution(* com.nextyu.demo.web.controller.*.*(..))")
  public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {

    System.out.println("time aspect start");

    Object[] args = pjp.getArgs();
    for (Object arg : args) {
      System.out.println("arg is " + arg);
    }

    long startTime = System.currentTimeMillis();

    Object object = pjp.proceed();

    long endTime = System.currentTimeMillis();
    System.out.println("time aspect consume " + (endTime - startTime) + " ms");

    System.out.println("time aspect end");

    return object;
  }

}

启动服务器,在浏览器输入:http://localhost:8080/hello?name=tom

可以在控制台输出如下结果:

time filter start
time interceptor preHandle
handler 类:com.nextyu.demo.web.controller.HelloController
handler 方法:sayHello
time aspect start
arg is tom
name: tom
time aspect consume 0 ms
time aspect end
time interceptor postHandle
time interceptor consume 2 ms
time interceptor afterCompletion
time filter consume 4 ms
time filter end

可以看到,filter 先执行,再到 interceptor 执行,再到 aspect 执行,再到真正执行 HelloController.sayHello() 方法。
我们也获取到了 HelloController.sayHello(String name) 方法参数 name 的值。

请求拦截过程图

graph TD
httprequest-->filter
filter-->interceptor
interceptor-->aspect
aspect-->controller
(0)

相关推荐

  • SpringBoot之Java配置的实现

    Java配置也是Spring4.0推荐的配置方式,完全可以取代XML的配置方式,也是SpringBoot推荐的方式. Java配置是通过@Configuation和@Bean来实现的: 1.@Configuation注解,说明此类是配置类,相当于Spring的XML方式 2.@Bean注解,注解在方法上,当前方法返回的是一个Bean eg: 此类没有使用@Service等注解方式 package com.wisely.heighlight_spring4.ch1.javaconfig; publ

  • Spring相关知识点的总结与梳理

    1).IOC:控制反转,某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定,即由Spring容器借由Bean配置来进行控制 2).DI:依赖注入,让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖 3).IOC主要分为3种类型:构造函数注入.属性注入和接口注入.Spring支持构造函数注入和属性注入 4).类装载器ClassLoader 类装载器就是寻找类的字节码文件并构造出类在JVM内部表示对象的组件.在Java中,类装载器把一个类装

  • Java五子棋AI实现代码

    思路: ①五子棋界面的实现 ②交互下棋的实现 ③重绘 ④AI,实现人机对战 五子棋和简单AI的实现: 首先将五子棋的界面写出来. 首先我们写一个接口类,定义好棋盘的数据(目的是方便修改). public interface Config { public static final int X0=50;//左上角起点X值 public static final int Y0=50;//左上角起点Y值 public static final int ROWS=15;//横向线数 public sta

  • Spring多对象引入方法

    在以前使用xml配置注入的时候, 可以通过name名称注入, 也可以使用type类型注入. 在SpringBoot中, 可以使用@Resource和@Autowried注解进行注入. @Resource 默认会使用名称进行注入,  如果找不到, 会使用自动使用类型进行注入. @Autowried, 则会在容器中寻找匹配的对象, 如果找到则注入成功, 如果没找到或者找到多个, 则会报错. 但是, 如果有多个的情况下, 可以使用@Qualifier("别名") 进行约束. 1.创建Bean

  • JavaTCP上传文本文件代码

    基于聊天客户端的基础上的文件(TXT文件)传输 客户端代码: public class UploadClient { public static void main(String[] args) throws UnknownHostException, IOException { // TODO Auto-generated method stub //1,创建socket客户端对象 Socket s = new Socket("localhost",10005); //2,读取本地文

  • 浅谈Java中Spring Boot的优势

    Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. Spring Boot 简化了基于 Spring 的应用开发,通过少量的代码就能创建一个独立的.产品级别的 Spring 应用. 作为一名 Java 程序员如果你已经厌恶了传统的开发模式,希望有一个全新的框架可以提供快速开发,简单集成的编程体验,强烈建议你学习了解 Spring Boot .

  • SpringBoot中关于static和templates的注意事项以及webjars的配置

    1. 默认情况下, 网页存放于static目录下, 默认的"/"指向的是~/resouces/static/index.html文 2. 如果引入了thymeleaf, 则默认指向的地址为~/resouces/templates/index.html <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymel

  • Spring Boot集成Java DSL的实现代码

    Spring Integration Java DSL已经融合到Spring Integration Core 5.0,这是一个聪明而明显的举动,因为: 基于Java Config启动新Spring项目的每个人都使用它 SI Java DSL使您可以使用Lambdas等新的强大Java 8功能 您可以使用 基于IntegrationFlowBuilder的Builder模式构建流 让我们看看基于ActiveMQ JMS的示例如何使用它. Maven依赖: <dependencies> <

  • java Springboot实现多文件上传功能

    前端采用layui框架,讲解多文件上传的完整实现功能. 前端html重点代码如下: <div class="layui-form-item"> <label class="layui-form-label">上传文件</label> <div class="layui-input-block"> <div class="layui-upload"> <butto

  • Spring Cloud原理详解

    之前一直在看<Spring Cloud微服务实战>,最近又看了架构笔记的<拜托!面试请不要再问我Spring Cloud底层原理>,对Spring Cloud的主要组件的原理有了更深的理解,特地做一下总结 一.Spring Cloud核心组件:Eureka (1)Netflix Eureka 1).Eureka服务端:也称服务注册中心,同其他服务注册中心一样,支持高可用配置.如果Eureka以集群模式部署,当集群中有分片出现故障时,那么Eureka就转入自我保护模式.它允许在分片故

随机推荐