1. 过滤器跟拦截器的区别
在说拦截器之前,不得不说一下过滤器,有时候往往被这两个词搞的头大。
其实我们最先接触的就是过滤器,还记得web.xml中配置的<filter>吗~
你应该知道spring mvc的拦截器是只拦截controller而不拦截jsp,html 页面文件的,如果想要拦截那怎么办?
这就用到过滤器filter了,filter是在servlet前执行的,你也可以理解成过滤器中包含拦截器,一个请求过来 ,先进行过滤器处理,看程序是否受理该请求 。
过滤器放过后 , 程序中的拦截器进行处理 。
(1)过滤器(Filter):当你有一堆东西的时候,你只希望选择符合你要求的某一些东西。定义这些要求的工具,就是过滤器。(理解:就是一堆字母中取一个B)
(2)拦截器(Interceptor):在一个流程正在进行的时候,你希望干预它的进展,甚至终止它进行,这是拦截器做的事情。(理解:就是一堆字母中,干预他,通过验证的少点,顺便干点别的东西)。
2. spring中的拦截器
在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、预先设置数据以及统计方法的执行效率等等。
今天就来详细的谈一下spring中的拦截器。spring中拦截器主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。
2.1 HandlerInterceptor拦截器
HandlerInterceptor是springMVC项目中的拦截器,它拦截的目标是请求的地址,比MethodInterceptor先执行。
实现一个HandlerInterceptor拦截器可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。
这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。
注册:
(1)通过spring.xml配置文件注册
拦截器类需要实现HandlerInterceptor接口,并且需要在servlet.xml中配置<mvc:interceptor>
(2)通过配置类进行注册
1.创建我们自己的拦截器类并实现 HandlerInterceptor 接口
2.创建一个Java类添加@Configuration并继承WebMvcConfigurerAdapter或WebMvcConfigurer,并重写 addInterceptors 方法。
3.实例化我们自定义的拦截器,然后将对像手动添加到拦截器链中(在addInterceptors方法中添加)
2.2 MethodInterceptor拦截器
继承AOP类
MethodInterceptor是AOP项目中的拦截器,它拦截的目标是方法,即使不是controller中的方法。实现MethodInterceptor拦截器大致也分为两种,
一种是实现MethodInterceptor接口,另一种利用AspectJ的注解或配置。
在invoke()方法中写拦截规则
注册:
(1)通过spring.xml配置文件注册
拦截器类需要实现MethodInterceptor接口,并且需要在.xml中配置<aop:config>,类似于AOP
(2)通过aop的@Aspect注解进行注册
类添加@Aspect注解与@Pointcut()切入点和方法上添加@Before()、@Around()等即可
经测试发现,interceptor先于AOP执行。
3. 谈一谈区别
上面的两种拦截器都能起到拦截的效果,但是他们拦截的目标不一样,实现的机制不同,所以有的时候适用不同的场景。
HandlerInterceptoer拦截的是请求地址,所以针对请求地址做一些验证、预处理等操作比较合适。
当你需要统计请求的响应时间时MethodInterceptor将不太容易做到,因为它可能跨越很多方法或者只涉及到已经定义好的方法中一部分代码。
MethodInterceptor利用的是AOP的实现机制,在本文中只说明了使用方式,关于原理和机制方面介绍的比较少,
因为要说清楚这些需要讲出AOP的相当一部分内容。在对一些普通的方法上的拦截HandlerInterceptoer就无能为力了,这时候只能利用AOP的MethodInterceptor。
-----------------------------------------------------------------------------------
过滤器
注册方式
(1)@WebFilter(urlpatters="/*")
该类需要实现Filter接口,重写doFilter()方法
在 @WebFilter 注解中可以配置过滤器的拦截规则。这个注解要生效,还需要我们在项目启动类上配置 @ServletComponentScan 注解
@ServletComponentScan 注解虽然名字带了 Servlet,但是实际上它不仅仅可以扫描项目中的 Servlet 容器,也可以扫描 Filter 和 Listener。
这是我们在 Spring Boot 中使用过滤器的第一种方式,在实际项目中,这种方式使用较少,因为这种方式有一个很大的弊端就是无法指定 Filter 的优先级,
如果存在多个 Filter 时,无法通过 @Order 指定优先级。
(2)@Bean
该类需要添加@Component将接口扫描到容器,并实现Filter接口
第二种方式就是将过滤器配置成 Bean,注册到 Spring 容器中去。
这种方法不再需要 @ServletComponentScan 注解,只要一个 Bean 即可
而且这种方式还有一个优势,就是如果存在多个 Filter,可以通过 @Order(-1) 注解指定多个 Filter 的优先级
虽然解决了优先级问题,但是这种方式没有办法设置 Filter 的拦截规则!
是的,直接定义 Bean 的话,默认的拦截规则就是 /* 即拦截所有请求,开发者无法进行自定义配置。
(3)FilterRegistrationBean
第三种方案还是将 Filter 封装成一个 Bean,但这个 Bean 是 FilterRegistrationBean,
通过 FilterRegistrationBean 我们既可以配置 Filter 的优先级,也可以配置 Filter 的拦截规则。
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<Filter> XXXPrecautionFilterRegistrationBean() {
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.addUrlPatterns(Arrays.asList("/hello/*"));// url拦截规则
filterRegistrationBean.setFilter(new XXXFilter());
filterRegistrationBean.setOrder(-2);
return filterRegistrationBean;
}
}
一般在项目中,我们都是使用 FilterRegistrationBean 来配置过滤器
-----------------------------------------------------------------------------------
AOP切面
@Aspect 就是把一个类定义为切面供容器读取。(类添加@Aspect注解)
@before: 前置通知,在方法执行之前执行。
@After:后置通知,在方法执行后执行。
@AfterReturning: 返回通知,在方法返回结果之后执行。
@AfterThrowing:异常通知,在方法抛出异常之后执行。
@Around:环绕通知,围绕着方法执行。
1.简单实现
@Component
@Aspect
public class TestAspect {
@Pointcut("execution( * com.atguigu.gulimall.product.controller.CouponController.*(..))")
private void log(){
}
@Before("log()")
public void TestBefore(JoinPoint joinPoint){
System.out.println("在调用方法的时候都会调用这个切面");
}
}
2.结合自定义注解实现
#1
@Documented
@Target({ElementType.METHOD}) // 在方法上加
@Retention(RetentionPolicy.RUNTIME) // 运行时
public @interface AnnotationTest {//自定义注解
}
#2
@RequestMapping("/test1")
@AnnotationTest
public String list(@RequestBody Map<String, Object> params){
return "ok";
}
#3
@Pointcut("@annotation(com.atguigu.gulimall.product.annoation.AnnotationTest)")
private void annotation(){
}
@After("annotation()")
public void testAnnotation(){
System.out.println("执行注解注释的方法后执行此方法");
}