上一篇文章中,我們學(xué)習(xí)了如何自定義一個(gè)Starter,而今天要給大家分享的是復(fù)雜點(diǎn)的自定義Starter。
需求
自定義一個(gè)記錄接口請(qǐng)求的Starter。
創(chuàng)建Starter項(xiàng)目
創(chuàng)建一個(gè)名為javafamily-log-spring-boot-starter項(xiàng)目。
1.引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>12345678910111213141516171819復(fù)制代碼類型:[java]
2.自定義日志注解
package com.javafamily.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;// 作用于方法級(jí)別@Target(ElementType.METHOD)// 設(shè)置注解生命周期@Retention(RetentionPolicy.RUNTIME)public @interface JavaFamilyLog { /** * 描述接口作用 */ String remark() default ""; /** * 是否開(kāi)啟時(shí)間打印 默認(rèn)開(kāi)啟 */ boolean enable() default true; }1234567891011121314151617181920212223復(fù)制代碼類型:[java]
注解里面包含兩個(gè)屬性,remark表示接口作用描述,enable表示是否開(kāi)啟接口耗時(shí)的記錄,默認(rèn)為開(kāi)啟,該屬性用于測(cè)試對(duì)比。
3.自定義攔截器
package com.javafamily.interceptor;import com.fasterxml.jackson.databind.ObjectMapper;import com.javafamily.annotation.JavaFamilyLog;import lombok.extern.slf4j.Slf4j;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.ModelAndView;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.lang.reflect.Method;@Slf4jpublic class JavaFamilyInterceptor extends HandlerInterceptorAdapter { // 記錄請(qǐng)求起始時(shí)間 private static final ThreadLocal<Long> START_TIME_THREAD_LOCAL = new ThreadLocal<>(); // 使用jackson序列化 private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); /** * 前置處理 * * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); // 獲取方法上的JavaFamilyLog注解 JavaFamilyLog javaFamilyLog = method.getAnnotation(JavaFamilyLog.class); if (javaFamilyLog != null) { // 開(kāi)啟打印功能才記錄時(shí)間 if (javaFamilyLog.enable()) { // 獲取當(dāng)前時(shí)間作為請(qǐng)求接口開(kāi)始時(shí)間 long startTime = System.currentTimeMillis(); // 將開(kāi)始時(shí)間存儲(chǔ)到ThreadLocal中 START_TIME_THREAD_LOCAL.set(startTime); } } } return true; } /** * 后置處理 * * @param request * @param response * @param handler * @param modelAndView * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); JavaFamilyLog javaFamilyLog = method.getAnnotation(JavaFamilyLog.class); if (javaFamilyLog != null) { // 定義方法執(zhí)行耗時(shí) long executionTime = -1; // 開(kāi)啟時(shí)間記錄 if (javaFamilyLog.enable()) { // 獲取當(dāng)前時(shí)間作為截止時(shí)間 long endTime = System.currentTimeMillis(); // 從獲取開(kāi)始時(shí)間 long startTime = START_TIME_THREAD_LOCAL.get(); START_TIME_THREAD_LOCAL.remove(); // 計(jì)算方法執(zhí)行耗時(shí) executionTime = endTime - startTime; } // 獲取請(qǐng)求路徑 String requestUri = request.getRequestURI(); // 獲取方法所在的類路徑以及方法名 通過(guò)#拼接 效果直觀 String methodFullPath = method.getDeclaringClass().getName() + "#" + method.getName(); // 獲取接口作用描述 String remark = javaFamilyLog.remark(); // 將參數(shù)轉(zhuǎn)換成字符串 String parameters = OBJECT_MAPPER.writeValueAsString(request.getParameterMap()); // 打印日志 log.info("\n接口作用描述:{}\n請(qǐng)求路徑: {}\n方法路徑: {}\n請(qǐng)求參數(shù):{}\n接口耗時(shí):{} ms", remark, requestUri, methodFullPath, parameters, executionTime == -1 ? null : executionTime); } } } }12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394復(fù)制代碼類型:[java]
自定義攔截器中使用jackson序列化對(duì)象,習(xí)慣fastjson的小伙伴可以相應(yīng)依賴替換。整體的思路就是被請(qǐng)求的方法上是否加了JavaFamilyLog注解,如果加了就對(duì)接口請(qǐng)求信息緯度進(jìn)行記錄,同時(shí)需要判斷時(shí)間記錄是否開(kāi)啟,如果不開(kāi)啟就不記錄。
4.添加自定義攔截器
package com.javafamily.config;import com.javafamily.interceptor.JavaFamilyInterceptor;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class JavaFamilyLogAutoConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 添加自己實(shí)現(xiàn)的攔截器 registry.addInterceptor(new JavaFamilyInterceptor()); } }1234567891011121314151617復(fù)制代碼類型:[java]
5.編寫(xiě)配置文件
在resources文件夾下創(chuàng)建META-INF文件夾,在META-INF文件夾下創(chuàng)建spring.factories文件。
org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.javafamily.config.JavaFamilyLogAutoConfiguration12復(fù)制代碼類型:[java]
項(xiàng)目整體如下:
引入自定義Starter
創(chuàng)建任意SpringBoot工程,引入javafamily-log-spring-boot-starter依賴。
<dependency> <groupId>com.javafamily</groupId> <artifactId>javafamily-log-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>12345復(fù)制代碼類型:[java]
創(chuàng)建HelloController,對(duì)剛寫(xiě)完的自定義Starter進(jìn)行測(cè)試。
package com.javafamily.familydemo.controller;import com.javafamily.annotation.JavaFamilyLog;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestControllerpublic class HelloController { @JavaFamilyLog(remark = "/關(guān)閉時(shí)間打印測(cè)試", enable = false) @GetMapping("/test") public String test() { return "success"; } @JavaFamilyLog(remark = "日志測(cè)試") @GetMapping("/logTest") public String logTest(String name) { return "Hello " + name; } }123456789101112131415161718192021222324復(fù)制代碼類型:[java]
接著分別請(qǐng)求兩個(gè)方法,我們看下效果。
2021-07-28 19:03:03.257 INFO 14580 --- [nio-8080-exec-6] c.j.interceptor.JavaFamilyInterceptor : 接口作用描述:/關(guān)閉時(shí)間打印測(cè)試 請(qǐng)求路徑: /test 方法路徑: com.javafamily.familydemo.controller.HelloController#test 請(qǐng)求參數(shù):{} 接口耗時(shí):null ms2021-07-28 19:03:04.677 INFO 14580 --- [nio-8080-exec-7] c.j.interceptor.JavaFamilyInterceptor : 接口作用描述:日志測(cè)試 請(qǐng)求路徑: /logTest 方法路徑: com.javafamily.familydemo.controller.HelloController#logTest 請(qǐng)求參數(shù):{"name":["JavaFamily"]} 接口耗時(shí):5 ms123456789101112復(fù)制代碼類型:[java]
根據(jù)效果,我們可以看到test接口耗時(shí)為null說(shuō)明enable屬性生效了,logTest接口也打印了請(qǐng)求參數(shù),說(shuō)明自定義Starter的功能都實(shí)現(xiàn)了。同時(shí)日志中也包含了接口請(qǐng)求一些重要的緯度信息,大家可以根據(jù)自己的需求再添加一些緯度。
好了,本篇文章到此結(jié)束,我們下次見(jiàn)。