SpringBoot 共有三种拦截http请求方式Filter,interceptor和aop。
1、FIlter
使用Filter的时候需要在Application启动类上加入@ServletComponentScan注解
import JAVAx.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 在SpringBoot中通过注解注册的方式简单的使用Filter
* @author rayfoo
*/
@WebFilter(urlPatterns = "/*", filterName = "MyFilter")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter Filter初始化中");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("MyFilter 开始进行过滤处理");
//调用该方法后,表示过滤器经过原来的url请求处理方法
chain.doFilter(request, response);
System.out.println("MyFilter 处理后的操作");
}
@Override
public void destroy() {
System.out.println("MyFilter Filter销毁中");
}
}
上述代码中,重写了Filter的三个方法,分别是:
init:在此Filter被创建时执行
doFilter:处理Filter的真正业务逻辑,可以在这个方法中对请求进行放行,在放行前后都可以执行代码,也可以在此方法中进行重定向和请求转发,但是一旦使用了请求转发、重定向,抛出异常,出现异常,被拦截的路径对应的业务方法就不会被执行。
destory:在此FIlter被销毁时执行
2、Interceptor
Interceptor依赖于web框架,我们经常在Spring MVC中用到该配置,在这个场景下Interceptor 就依赖于SpringMVC框架。
使用Interceptor之前要先创建类并实现HandlerInterceptor接口
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
/**
* 在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor 方法执行之前...");
return true;
}
/**
* 在业务处理器处理请求执行完成后,生成视图之前执行。后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor 回调操作...");
}
/**
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面)
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.out.println("MyInterceptor 方法执行之后...");
}
}
创建完实现类还需要注册Interceptor
在SpringBoot2中,建议使用的注册拦截器的方法有如下两种:
1)实现WebMvcConfigurer接口
2)继承WebMvcConfigurerAdapter类(此类也是实现了WebMvcConfigurer)
下面介绍一下实现WebMvcConfigurer方法注册拦截器
import com.xa.filter.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//1.得到自定义的拦截器
InterceptorRegistration interceptor = registry.addInterceptor(new MyInterceptor());
//2.需要拦截的路径 /**所有资源都拦截
interceptor.addPathPatterns("/**");
//3.设置不拦截的路径 (登录页需要的和静态资源)
interceptor.excludePathPatterns("/","/index.html","/login","/css/**","/img/**","/js/**");
}
}
3、AOP
使用AOP首先要引入jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
定义切面
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
@Aspect
@Component
@Order(1) //指定切面类执行顺序,数字越小越先执行
public class MyAspect {
/**
* 定义那个包下的执行 面向切换
* 该示例 实现 所有controller 包下的方法执行的切面
* 第一个*含义是:代表所有类型的返回值
* 第二个*是代表com.mall.web.controller包下的所有类
* 第三个是类下的所有方法,括号中两个点表示任意个形参
*/
@Pointcut("execution(public * com.*.controller.*.*(..))")
public void webLog(){}
/**
* 请求之前 进入控制之前 执行,但是在拦截器之后执行
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void deBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("MyAspect URL : " + request.getRequestURL().toString());
System.out.println("MyAspect HTTP_METHOD : " + request.getMethod());
System.out.println("MyAspect IP : " + request.getRemoteAddr());
System.out.println("MyAspect CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
System.out.println("MyAspect ARGS : " + Arrays.toString(joinPoint.getArgs()));
}
/**
* 控制器执行之后执行,得到控制器方法的返回值
* @param ret
* @throws Throwable
*/
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
System.out.println("MyAspect 方法的返回值 : " + ret);
}
//后置异常通知
@AfterThrowing("webLog()")
public void throwss(JoinPoint jp){
System.out.println("MyAspect 方法异常时执行.....");
}
//后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
@After("webLog()")
public void after(JoinPoint jp){
System.out.println("MyAspect 方法最后执行.....");
}
//环绕通知,环绕增强,相当于 请求方法的拦截器
@Around("webLog()")
public Object arround(ProceedingJoinPoint pjp) {
System.out.println("MyAspect 方法环绕start....."); // 最先执行
try {
Object o = pjp.proceed();
System.out.println("MyAspect 方法环绕proceed,结果是 :" + o); // 最后执行
return o;
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
}
我们新增个Controller来测试下他们的运行顺序
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class DemoController {
@GetMapping("/demo")
public Map test() {
Map map = new HashMap() ;
map.put("a","123") ;
return map ;
}
}
我们访问http://localhost:{port}/demo可以看到后台打印如下日志
可以看出他们的拦截顺序:filter—>Interceptor—->@Aspect 。以下这个图可以比较直观看出他们之间的拦截顺序
总结: 过滤器和拦截器的区别,过滤器是依赖于servlet容器(struts),在实现上基于函数回调,可以对几乎所有的请求进行过滤。但缺点是一个过滤器实例只能在容器初始化时调用一次,来进行过滤操作,获得我们想要的数据。
三者之间主要还是粒度的差异,应用场景的不同。
过滤器(Filter) :采用filter过滤器,可以获取http、http请求和响应,但无法获取与spring框架相关的信息,如哪个control处理,哪个方法处理,有哪些参数,这些都是无法获取的。
拦截器(Interceptor):采用interceptor拦截器,除了获取http、http请求和响应对象,还可以获取请求的类名、方法名,但拦截器无法获取请求参数的值。
切片 (Aspect) : 切片 aspect 可以获取传入的参数值,但无法获取原始的http,http请求和响应对象。