spring aop(MethodInterceptor), springmvc (HandlerInterceptor), servlet Filter有什么区别?

在spring web mvc中实现拦截功能时,有三种方式

  1. 使用功能servlet filter

  2. 使用springmvc 提供的 HandlerInterceptor

  3. 使用spring core 提供的 MethodInterceptor(spring aop)

拦截方式特点
servlet的过滤器Filter可以拿到原始的http请求和响应的信息,但是拿不到真正处理这个请求的方法的信息
springmvc的interceptor在Filter中是不知道这个请求是哪个控制器的哪个方法来处理的。如果你需要这个信息的话,那么需要使用springmvc的interceptor。 拦截器可以拿到原始的http请求和响应的信息,也能拿到真正处理这个请求的方法的信息,但是其拿不到这个方法被调用的时候真正调用的参数的值
spring的切片Aspect可以拿到方法被调用的时候真正调用的参数的值,但是拿不到原始的http请求和响应的信息

servlet的过滤器Filter

  1. 我们自己写的Filter类,Filter是Servlet规范的一部分,是Servlet容器(如Tomcat)实现的。
    1. 在spring boot下注册一个filter的三种方式(servlet、listener也是如此)
      1. 方式1:可以使用@WebFilter+@ServletComponentScan的方式
      2. 方式2:可以使用FilterRegistrationBean 进行API级别的注册,注意,在这种情况下可以对Filter order进行设置,而使用spring @Order注解是无效的
      3. 方式3:创建一个类去实现 ServletContextInitializer 接口,并把它注册为一个 Bean,Spring Boot 会负责调用这个接口的 onStartup 方法。
@Bean
public ServletRegistrationBean asyncServletServletRegistrationBean(){
    ServletRegistrationBean registrationBean =  new ServletRegistrationBean(new AsyncServlet(),"/");
    registrationBean.setName("MyAsyncServlet");
    registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return registrationBean;
}

方式三

@Bean
public ServletContextInitializer servletContextInitializer() {
    return servletContext -> {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        FilterRegistration.Dynamic registration = servletContext.addFilter("filter", filter);
        registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/");
    };
}

FilterRegistrationBean其实也是通过 ServletContextInitializer 来实现的,它实现了 ServletContextInitializer 接口

特点:

可以拿到原始的http请求和响应的信息,但是拿不到真正处理这个请求的方法的信息

存在的问题:

  • 通过Filter只能拿到http的请求和响应,只能从请求和响应中获得一些参数。
  • 当前发过来的这个请求实际上真正是由哪个控制器的哪个方法来处理的,在Filter里面是不知道的,因为javax.servlet.Filter是J2EE规范中定义的,J2EE规范里面实际上并不知道与spring相关的任何内容。
  • 而我们的controller实际上是spring mvc定义的一套机制。如果你需要这些信息,那么就需要使用拦截器Interceptor

springmvc的拦截器Interceptor

public class TimeInterceptor implements HandlerInterceptor {}


public interface HandlerInterceptor {

// 最后一个参数,Object handler,这个是 真正用来处理这个请求的Controller的方法声明
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}

	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

// 其中的Exception就是表示其是否发生了异常
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}
  • 在我们真正访问的Controller的某个方法被调用之前,会调用preHandler方法
  • 在Controller的方法调用之后,会调用postHandler方法
  • 如果你的controller中的方法抛出了异常,那么postHandler这个方法不会被调用。
  • 但无论controller中的方法是否抛出异常,afterCompletion方法都是会被调用的。

特点:

  1. 拦截器可以拿到原始的http请求和响应的信息,也能拿到真正处理这个请求的方法的信息,但是其拿不到这个方法被调用的时候真正调用的参数的值
  2. 通过handler虽然可以拿到处理此请求的controller和method,但是其没办法拿到这个method的真正的参数的值
  3. 这个handler只是那个方法的声明,但实际调用的时候,比如说调用getInfo的时候,我的这个id传的是多少,在拦截器里面用handler是拿不到的
  4. 为什么会这样呢? 从源码上看一下
    1. {@link org.springframework.web.servlet.DispatcherServlet#doDispatch}
				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = HttpMethod.GET.matches(method);
				if (isGet || HttpMethod.HEAD.matches(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
// 这个方法就是调用我们拦截器的preHandler方法
// 如果我们的interceptor的preHandler返回的是false,就直接return掉不进行处理,否则执行目标controller的目标method
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 而方法参数的拼装是在ha.handle这个方法中完成的,
// 所谓的方法参数的拼装就是把我请求中的参数组装成我们controller的method中所要的参数/对象

// 另外,在interceptor中可以指定指定的url才需要拦截

spring aop

  1. 在spring中,Advice的实现接口就是Interceptor
  2. 注意,Advice和Interceptor都是在org.aopalliance包下,并非在spring包下,为spring引入的外部依赖
  3. Interceptor接口在spring中的实现类MethodInterceptor,通过before, after, afterReturing, afterThrowing等增强器来对目标方法进行增强

特点

  1. 可以拿到方法被调用的时候真正调用的参数的值
  2. 但是拿不到原始的http请求和响应的信息

Filter最先其作用

之后是Interceptor

再之后才是Aspect

之后才会进入到Controller中去

可以看到springmvc中的拦截器是用来拦截请求的,在MethodInterceptor之前执行

而MethodInterceptor是spring AOP项目中的拦截器,它拦截的目标是方法,即使其不是Controller中的请求方法。