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 開始進行過濾處理");
//調(diào)用該方法后,表示過濾器經(jīng)過原來的url請求處理方法
chain.doFilter(request, response);
System.out.println("MyFilter 處理后的操作");
}
@Override
public void destroy() {
System.out.println("MyFilter Filter銷毀中");
}
}
上述代碼中,重寫了Filter的三個方法,分別是:
init:在此Filter被創(chuàng)建時執(zhí)行
doFilter:處理Filter的真正業(yè)務邏輯,可以在這個方法中對請求進行放行,在放行前后都可以執(zhí)行代碼,也可以在此方法中進行重定向和請求轉發(fā),但是一旦使用了請求轉發(fā)、重定向,拋出異常,出現(xiàn)異常,被攔截的路徑對應的業(yè)務方法就不會被執(zhí)行。
destory:在此FIlter被銷毀時執(zhí)行
2、Interceptor
Interceptor依賴于web框架,我們經(jīng)常在Spring MVC中用到該配置,在這個場景下Interceptor 就依賴于SpringMVC框架。
使用Interceptor之前要先創(chuàng)建類并實現(xiàn)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 {
/**
* 在業(yè)務處理器處理請求之前被調(diào)用。預處理,可以進行編碼、安全控制、權限校驗等處理
* @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 方法執(zhí)行之前...");
return true;
}
/**
* 在業(yè)務處理器處理請求執(zhí)行完成后,生成視圖之前執(zhí)行。后處理(調(diào)用了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 回調(diào)操作...");
}
/**
* 在DispatcherServlet完全處理完請求后被調(diào)用,可用于清理資源等。返回處理(已經(jīng)渲染了頁面)
* @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 方法執(zhí)行之后...");
}
}
創(chuàng)建完實現(xiàn)類還需要注冊Interceptor
在SpringBoot2中,建議使用的注冊攔截器的方法有如下兩種:
1)實現(xiàn)WebMvcConfigurer接口
2)繼承WebMvcConfigurerAdapter類(此類也是實現(xiàn)了WebMvcConfigurer)
下面介紹一下實現(xiàn)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.設置不攔截的路徑 (登錄頁需要的和靜態(tài)資源)
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) //指定切面類執(zhí)行順序,數(shù)字越小越先執(zhí)行
public class MyAspect {
/**
* 定義那個包下的執(zhí)行 面向切換
* 該示例 實現(xiàn) 所有controller 包下的方法執(zhí)行的切面
* 第一個*含義是:代表所有類型的返回值
* 第二個*是代表com.mall.web.controller包下的所有類
* 第三個是類下的所有方法,括號中兩個點表示任意個形參
*/
@Pointcut("execution(public * com.*.controller.*.*(..))")
public void webLog(){}
/**
* 請求之前 進入控制之前 執(zhí)行,但是在攔截器之后執(zhí)行
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void deBefore(JoinPoint joinPoint) throws Throwable {
// 接收到請求,記錄請求內(nèi)容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 記錄下請求內(nèi)容
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()));
}
/**
* 控制器執(zhí)行之后執(zhí)行,得到控制器方法的返回值
* @param ret
* @throws Throwable
*/
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 處理完請求,返回內(nèi)容
System.out.println("MyAspect 方法的返回值 : " + ret);
}
//后置異常通知
@AfterThrowing("webLog()")
public void throwss(JoinPoint jp){
System.out.println("MyAspect 方法異常時執(zhí)行.....");
}
//后置最終通知,final增強,不管是拋出異?;蛘哒M顺龆紩?zhí)行
@After("webLog()")
public void after(JoinPoint jp){
System.out.println("MyAspect 方法最后執(zhí)行.....");
}
//環(huán)繞通知,環(huán)繞增強,相當于 請求方法的攔截器
@Around("webLog()")
public Object arround(ProceedingJoinPoint pjp) {
System.out.println("MyAspect 方法環(huán)繞start....."); // 最先執(zhí)行
try {
Object o = pjp.proceed();
System.out.println("MyAspect 方法環(huán)繞proceed,結果是 :" + o); // 最后執(zhí)行
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 。以下這個圖可以比較直觀看出他們之間的攔截順序
總結: 過濾器和攔截器的區(qū)別,過濾器是依賴于servlet容器(struts),在實現(xiàn)上基于函數(shù)回調(diào),可以對幾乎所有的請求進行過濾。但缺點是一個過濾器實例只能在容器初始化時調(diào)用一次,來進行過濾操作,獲得我們想要的數(shù)據(jù)。
三者之間主要還是粒度的差異,應用場景的不同。
過濾器(Filter) :采用filter過濾器,可以獲取http、http請求和響應,但無法獲取與spring框架相關的信息,如哪個control處理,哪個方法處理,有哪些參數(shù),這些都是無法獲取的。
攔截器(Interceptor):采用interceptor攔截器,除了獲取http、http請求和響應對象,還可以獲取請求的類名、方法名,但攔截器無法獲取請求參數(shù)的值。
切片 (Aspect) : 切片 aspect 可以獲取傳入的參數(shù)值,但無法獲取原始的http,http請求和響應對象。