一,filter/interceptor/aop生效的先后順序?
1,filter即過濾器,基於servlet容器,處於最外層,
所以它會最先起作用,最后才停止
說明:filter對所有訪問到servlet容器的url都有效,包括靜態資源
2,interceptor即攔截器,基於web框架,它會在filter之后起作用
說明:spring boot 1.x中,靜態資源已被interceptor排除,
spring boot 2.x中,需要自己手動排除到靜態資源的訪問
filter和interceptor都是作用於請求
3,aop即切面,基於Spring的IOC容器,對spring管理的bean有效,
它會在interceptor之后才生效
aop可以作用於類和方法
如圖:
說明:劉宏締的架構森林是一個專注架構的博客,地址:https://www.cnblogs.com/architectforest
對應的源碼可以訪問這里獲取: https://github.com/liuhongdi/
說明:作者:劉宏締 郵箱: 371125307@qq.com
二,演示項目的相關信息
1,項目地址:
https://github.com/liuhongdi/costtime
2,項目的原理:
項目中使用了兩個filter,兩個interceptor,兩個aspect,
功能分別都是:計算請求或方法執行的時間, 打印請求的參數
然后觀察它們被執行到的順序
3,項目結構:如圖:
三,配置文件說明 :
pom.xml
<!--aop begin--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
用來引入aop
四,java代碼說明
1,DefaultMvcConfig.java
@Configuration @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) public class DefaultMvcConfig implements WebMvcConfigurer { @Resource private LogInterceptor logInterceptor; @Resource private CostTimeInterceptor costTimeInterceptor; @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("/home/home"); } //添加Interceptor @Override public void addInterceptors(InterceptorRegistry registry) { //1.加入的順序就是攔截器執行的順序, //2.按順序執行所有攔截器的preHandle //3.所有的preHandle 執行完再執行全部postHandle 最后是postHandle registry.addInterceptor(costTimeInterceptor) .addPathPatterns("/home/home**") .excludePathPatterns("/html/*","/js/*"); registry.addInterceptor(logInterceptor) .addPathPatterns("/**") .excludePathPatterns("/html/*","/static/**","/images/**"); } //add filter @Bean public FilterRegistrationBean addTimeFilterBean() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new TimeFilter()); registration.setName("timeFilter"); registration.setOrder(2); //請求中過濾器執行的先后順序,值越小越先執行 registration.addUrlPatterns("/home/*","/abc/*"); return registration; } @Bean public FilterRegistrationBean addLogFilterBean() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new LogFilter()); registration.setName("logFilter"); registration.setOrder(1); //請求中過濾器執行的先后順序,值越小越先執行 registration.addUrlPatterns("/*"); registration.addInitParameter("exclusions","/js/*,/images/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid2/*"); return registration; } }
說明:攔截器是按加入到registry的順序執行
filter是按setOrder中指定的順序執行
另外:filter也可以用Order注解來指定順序
2,CostTimeAspect.java
@Component @Aspect @Order(3) public class CostTimeAspect { ThreadLocal<Long> startTime = new ThreadLocal<>(); @Pointcut("execution(public * com.costtime.demo.controller.*.*(..))") private void pointcut() {} //用around得到方法使用的時間 @Around(value = "pointcut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("------costtime aop around begin"); long begin = System.nanoTime(); Object obj=joinPoint.proceed(); long end =System.nanoTime(); long timeMicro = (end-begin)/1000; System.out.println("costtime aop 方法around:微秒數:"+timeMicro); System.out.println("------costtime aop around end"); return obj; } @Before("pointcut()") public void doBefore(JoinPoint joinPoint) throws Throwable{ System.out.println("------costtime aop doBefore begin"); startTime.set(System.currentTimeMillis()); } //和doBefore搭配,得到使用的時間 @AfterReturning(returning = "ret" , pointcut = "pointcut()") public void doAfterReturning(Object ret){ System.out.println("------costtime aop doAfterReturning begin"); System.out.println("costtime aop 方法doafterreturning:毫秒數:"+ (System.currentTimeMillis() - startTime.get())); } }
3,LogAspect.java
@Component @Aspect @Order(1) public class LogAspect { @Pointcut("execution(public * com.costtime.demo.controller.*.*(..))") private void pointcut() {} @Around(value = "pointcut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("------log aop around begin"); Object obj=joinPoint.proceed(); System.out.println("------log aop around end"); return obj; } //把請求參數打印出來 @Before("pointcut()") public void doBefore(JoinPoint joinPoint) throws Throwable{ System.out.println("------log aop doBefore begin"); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //得到方法的參數名和參數值 Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature)signature; String[] paramNames = methodSignature.getParameterNames(); Object[] args = joinPoint.getArgs(); String paramValue = ""; Map<String, Object> nameAndArgs = new HashMap<String, Object>(); for (int i = 0; i < paramNames.length; i++) { paramValue+="parameter:"+paramNames[i]+";value:"+args[i]; } // 記錄下請求內容 System.out.println("log aop URL : " + request.getRequestURL().toString()); System.out.println("log aop PARAM : " + request.getQueryString()); System.out.println("log aop HTTP_METHOD : " + request.getMethod()); System.out.println("log aop IP : " + request.getRemoteAddr()); System.out.println("log aop METHOD CLASS : " + joinPoint.getSignature().getDeclaringTypeName() ); System.out.println("log aop METHOD NAME: " + joinPoint.getSignature().getName()); System.out.println("log aop METHOD ARGS : " + paramValue); } @AfterReturning(returning = "ret" , pointcut = "pointcut()") public void doAfterReturning(Object ret){ System.out.println("------log aop doAfterReturning begin"); } }
說明:這兩個aspect用Order注解來指定執行的先后順序,
值越小執行順序越靠前
4,過濾器和攔截器的代碼因為功能基本一致,為節省篇幅,不再貼出,
大家可以從github上訪問:
https://github.com/liuhongdi/costtime
五,測試啟動順序的效果
1,訪問url:
http://127.0.0.1:8080/home/home?v=1
查看控制台的輸出:
----------------log filter doFilter begin ===執行過濾器功能 log filter URL : http://127.0.0.1:8080/home/home log filter PARAM : v=1 log filter HTTP_METHOD : GET log filter IP : 127.0.0.1 ----------------time filter doFilter begin---------------time interceptor preHandle [interceptor] request parameters: name:v;value:1 ---------------log interceptor preHandle [interceptor] request parameters: name:v;value:1------log aop around begin ------log aop doBefore begin log aop URL : http://127.0.0.1:8080/home/home log aop PARAM : v=1 log aop HTTP_METHOD : GET log aop IP : 127.0.0.1 log aop METHOD CLASS : com.costtime.demo.controller.HomeController log aop METHOD NAME: homeMethod log aop METHOD ARGS : parameter:version;value:1 ------costtime aop around begin ------costtime aop doBefore begin ------costtime aop doAfterReturning begin costtime aop 方法doafterreturning:毫秒數:1027 costtime aop 方法around:微秒數:1027548 ------costtime aop around end ------log aop doAfterReturning begin ------log aop around end ---------------log interceptor postHandle ---------------time interceptor postHandle time interceptor 方法 postHandle:毫秒數:1108 ---------------log interceptor afterCompletion ---------------time interceptor afterCompletion timefilter: /home/home costtime: 1128ms ----------------time filter doFilter end ----------------log filter doFilter end
2,可以看到:
大類的啟動順序是:
filter
interceptor
aop
3,可以看到
filter的啟動順序,是按我們在config中設定的order順序
interceptor的啟動順序,是addInterceptor到registry的順序
同一個interceptor內部:執行順序是: preHandle,postHandle,afterCompletion
aop的啟動順序:是我們在Order注解中指定的順序
同一個aop內部:around比dobefore啟動更早
六,查看spring boot的版本
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.1.RELEASE)