應用(記錄用戶操作日志):
有時候我們需要處理一些請求日志,或者對某些方法進行一些監控,如果出現例外情況應該進行怎么樣的處理,現在,我們從spring boot中引入AOP
環境:idea、jdk 1.8、springboot、mysql
1.1 目錄結構
└─src └─main ├─java │ └─com │ └─example │ └─log │ │ LogApplication.java │ ├─annotation │ │ Log.java │ ├─aspect │ │ LogAspect.java │ ├─common │ │ ├─context │ │ │ BaseContext.java │ │ │ CallBack.java │ │ │ SpringContextHolder.java │ │ ├─enums │ │ │ Action.java │ │ └─utils │ │ CloseUtil.java │ │ ExceptionUtil.java │ │ FileUtil.java │ │ ServletUtil.java │ ├─controller │ │ LogController.java │ ├─domain │ │ SysLog.java │ ├─repository │ │ LogRepository.java │ └─service │ │ LogService.java │ └─impl │ LogServiceImpl.java └─resources │ application.yml └─ip2region ip2region.db (注意這個文件,作用:轉換IP地址來源)
resources/ip2region/ip2region.db 文件 下載:https://files.cnblogs.com/files/mmdz/ip2region.db.rar
1.2 日志表
准備sys_log日志表(mysql)
DROP TABLE IF EXISTS `sys_log`; CREATE TABLE `sys_log` ( `id` char(20) NOT NULL COMMENT '編號', `operator` varchar(255) DEFAULT NULL COMMENT '操作人', `operation_time` datetime DEFAULT NULL COMMENT '操作時間', `title` varchar(255) DEFAULT NULL COMMENT '編號', `method` varchar(255) DEFAULT NULL COMMENT '方法', `type` varchar(255) DEFAULT NULL COMMENT '請求方式', `params` varchar(255) DEFAULT NULL COMMENT '參數', `state` bit(1) DEFAULT NULL COMMENT '狀態', `action` varchar(255) DEFAULT NULL COMMENT '操作', `request_ip` varchar(255) DEFAULT NULL COMMENT '請求ip', `address` varchar(255) DEFAULT NULL COMMENT 'ip來源', `browser` varchar(255) DEFAULT NULL COMMENT '瀏覽器', `time` bigint(10) DEFAULT NULL COMMENT '請求耗時', `error` text COMMENT '異常信息', `system` varchar(255) DEFAULT NULL COMMENT '操作系統', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
1.3 pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.3</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>log</artifactId> <version>0.0.1-SNAPSHOT</version> <name>log</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <datasource.version>3.3.1</datasource.version> <mysql.version>8.0.22</mysql.version> <mybatis.plus.version>3.4.3</mybatis.plus.version> <swagger.version>2.9.2</swagger.version> <hutool.version>5.5.7</hutool.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 切 面 編 程 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- 數 據 庫 操 作 框 架 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis.plus.version}</version> </dependency> <!-- 數 據 庫 連 接 工 具 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> <scope>runtime</scope> </dependency> <!-- 常 用 工 具 類 --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool.version}</version> </dependency> <!-- Swagger UI 相關 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>${swagger.version}</version> <exclusions> <exclusion> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> </exclusion> <exclusion> <groupId>io.swagger</groupId> <artifactId>swagger-models</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>${swagger.version}</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.5.21</version> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-models</artifactId> <version>1.5.21</version> </dependency> <!-- 解析客戶端操作系統、瀏覽器信息 --> <dependency> <groupId>nl.basjes.parse.useragent</groupId> <artifactId>yauaa</artifactId> <version>5.23</version> </dependency> <!-- 根據IP獲取城市-Java調用“ip2region” --> <dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2region</artifactId> <version>1.7.2</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
1.4 application.yml
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/log?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT username: root password: 123456
2、編碼
2.1 domain包
SysLog(日志模型)
package com.example.log.domain; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.example.log.common.enums.Action; import lombok.Data; import java.io.Serializable; import java.time.LocalDateTime; /** * 日志模型 * */ @Data @TableName("sys_log") public class SysLog implements Serializable { /** 編號 */ @TableId("id") private String id; /** 操作人 */ @TableField("operator") private String operator; /** 操作時間 */ @TableField("operation_time") private LocalDateTime operationTime; /** 標題 */ @TableField("title") private String title; /** 請求方法 */ @TableField("method") private String method; /** 請求方式 */ @TableField("type") private String type; /** 請求參數 */ @TableField("params") private String params; /** 狀態(是否成功) */ @TableField("state") private Boolean state; /** 操作類型 */ @TableField("action") private Action action; /** 請求ip */ @TableField("request_ip") private String requestIp; /** ip來源 */ @TableField("address") private String address; /** 瀏覽器 */ @TableField("browser") private String browser; /** 請求耗時 */ @TableField("time") private Long time; /** 異常信息 */ @TableField("error") private byte[] error; @TableField(exist = false) private String exceptionDetailStr; /** 系統 */ @TableField("`system`") private String system; public String getExceptionDetailStr() { return new String(ObjectUtil.isNotNull(error) ? error : "".getBytes()); } }
2.2 annotation包
package com.example.log.annotation; import com.example.log.common.enums.Action; import java.lang.annotation.*; /** * 日志 注解 * */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Log { /** * Title 默認輸入 * */ String title() default "暫無標題"; /** * Describe 默認輸入 * */ String describe() default "暫無描述"; /** * Action 操作類型 * */ Action action() default Action.QUERY; }
2.3 common包
2.3.1 enums
Action 日 志 分 類
package com.example.log.common.enums; /** * 日 志 分 類 */ public enum Action { /** 認證 */ AUTH, /** 增 */ ADD, /** 刪 */ REMOVE, /** 改 */ EDIT, /** 查 */ QUERY, /** 導入 */ IMPORT, /** 導出 */ REPORT, /** 上傳 */ UPLOAD }
2.3.2 context
CallBack
package com.example.log.common.context; /** * @Desc: TODO * 針對某些初始化方法,在SpringContextHolder 初始化前時,<br> * 提交一個 提交回調任務。<br> * 在SpringContextHolder 初始化后,進行回調使用 */ public interface CallBack { /** * 回調執行方法 */ void executor(); /** * 本回調任務名稱 * @return / */ default String getCallBackName() { return Thread.currentThread().getId() + ":" + this.getClass().getName(); } }
SpringContextHolder
package com.example.log.common.context; import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import java.util.ArrayList; import java.util.List; public class SpringContextHolder { private static ApplicationContext applicationContext = null; private static final List<CallBack> CALL_BACKS = new ArrayList<>(); private static boolean addCallback = true; /** * 針對 某些初始化方法,在SpringContextHolder 未初始化時 提交回調方法。 * 在SpringContextHolder 初始化后,進行回調使用 * * @param callBack 回調函數 */ public synchronized static void addCallBacks(CallBack callBack) { if (addCallback) { SpringContextHolder.CALL_BACKS.add(callBack); } else { System.out.println("CallBack:" + callBack.getCallBackName() +" 已無法添加!立即執行"); callBack.executor(); } } /** * 獲取SpringBoot 配置信息 * * @param property 屬性key * @param defaultValue 默認值 * @param requiredType 返回類型 * @return / */ public static <T> T getProperties(String property, T defaultValue, Class<T> requiredType) { T result = defaultValue; try { result = getBean(Environment.class).getProperty(property, requiredType); } catch (Exception ignored) {} return result; } /** * 從靜態變量applicationContext中取得Bean, 自動轉型為所賦值對象的類型. */ public static <T> T getBean(Class<T> requiredType) { assertContextInjected(); return applicationContext.getBean(requiredType); } /** * 檢查ApplicationContext不為空. */ private static void assertContextInjected() { if (applicationContext == null) { throw new IllegalStateException("applicaitonContext屬性未注入, 請在applicationContext" + ".xml中定義SpringContextHolder或在SpringBoot啟動類中注冊SpringContextHolder."); } } }
BaseContext
package com.example.log.common.context; import com.example.log.common.utils.ServletUtil; import com.example.log.domain.SysLog; import com.example.log.common.enums.Action; import com.example.log.service.LogService; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.time.LocalDateTime; /** * Base Context */ @Component public class BaseContext { /** * 日 志 服 務 */ @Resource private LogService sysLogService; /** * 新增日志 * * @param title 標題 * @param methodName 請求方法 * @param parameter 參數 * @param action 動作 * @param state 狀態 * @param time 請求耗時 * @param error 異常 */ @Async @Transactional(rollbackFor = Exception.class) public void record(String title, String methodName, String parameter, Action action, Boolean state, Long time, byte[] error) { SysLog sysLog = new SysLog(); sysLog.setOperator("");// 操作人 sysLog.setOperationTime(LocalDateTime.now());// 操作時間 sysLog.setTitle(title);//標題 sysLog.setMethod(methodName);// 請求方法 sysLog.setType(ServletUtil.getMethod());// 請求方式 sysLog.setParams(parameter);// 參數 sysLog.setState(state);// 狀態(是否成功) sysLog.setAction(action);// 操作類型 String ip = ServletUtil.getIp(); sysLog.setRequestIp(ServletUtil.getIp());// 請求ip sysLog.setAddress(ServletUtil.getCityInfo(ip));// ip來源 sysLog.setBrowser(ServletUtil.getBrowser());// 瀏覽器 sysLog.setTime(time);// 請求耗時 sysLog.setError(error);// 異常信息 sysLog.setSystem(ServletUtil.getSystem());// 操作系統 sysLogService.save(sysLog); } }
2.3.3 utils
CloseUtil
package com.example.log.common.utils; import java.io.Closeable; /** * @Desc: TODO 用於關閉各種連接,缺啥補啥 */ public class CloseUtil { public static void close(Closeable closeable) { if (null != closeable) { try { closeable.close(); } catch (Exception e) { // 靜默關閉 } } } public static void close(AutoCloseable closeable) { if (null != closeable) { try { closeable.close(); } catch (Exception e) { // 靜默關閉 } } } }
ExceptionUtil
package com.example.log.common.utils; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; /** * @Desc: TODO 將日志堆棧信息輸出到文件 */ public class ExceptionUtil { public static String getMessage(Exception e) { StringWriter sw = null; PrintWriter pw = null; try { sw = new StringWriter(); pw = new PrintWriter(sw); // 將出錯的棧信息輸出到printWriter中 e.printStackTrace(pw); pw.flush(); sw.flush(); } finally { if (sw != null) { try { sw.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (pw != null) { pw.close(); } } return sw.toString(); } /** * 獲取堆棧信息 */ public static String getStackTrace(Throwable throwable){ StringWriter sw = new StringWriter(); try (PrintWriter pw = new PrintWriter(sw)) { throwable.printStackTrace(pw); return sw.toString(); } } }
FileUtil
package com.example.log.common.utils; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; /** * @Desc: TODO File工具類,擴展 hutool 工具包 */ public class FileUtil extends cn.hutool.core.io.FileUtil { /** * 系統臨時目錄 * <br> * windows 包含路徑分割符,但Linux 不包含, * 在windows \\==\ 前提下, * 為安全起見 同意拼裝 路徑分割符, * <pre> * java.io.tmpdir * windows : C:\Users/xxx\AppData\Local\Temp\ * linux: /temp * </pre> */ public static final String SYS_TEM_DIR = System.getProperty("java.io.tmpdir") + File.separator; /** * inputStream 轉 File */ public static File inputStreamToFile(InputStream ins, String name){ File file = new File(SYS_TEM_DIR + name); if (file.exists()) { return file; } OutputStream os = null; try { os = new FileOutputStream(file); int bytesRead; int len = 8192; byte[] buffer = new byte[len]; while ((bytesRead = ins.read(buffer, 0, len)) != -1) { os.write(buffer, 0, bytesRead); } } catch (Exception e) { e.printStackTrace(); } finally { CloseUtil.close(os); CloseUtil.close(ins); } return file; } }
ServletUtil
package com.example.log.common.utils; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONUtil; import com.example.log.common.context.SpringContextHolder; import nl.basjes.parse.useragent.UserAgent; import nl.basjes.parse.useragent.UserAgentAnalyzer; import org.lionsoul.ip2region.DataBlock; import org.lionsoul.ip2region.DbConfig; import org.lionsoul.ip2region.DbSearcher; import org.springframework.core.io.ClassPathResource; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.net.InetAddress; import java.net.UnknownHostException; /** * Servlet 工具類 * */ public class ServletUtil { /** * IP歸屬地查詢 */ private static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp?ip=%s&json=true"; /** * 用於IP定位轉換 */ private static final String REGION = "內網IP|內網IP"; private static boolean ipLocal = false; private static File file = null; private static DbConfig config; private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer .newBuilder() .hideMatcherLoadStats() .withCache(10000) .withField(UserAgent.AGENT_NAME_VERSION) .build(); static { SpringContextHolder.addCallBacks(() -> { ipLocal = SpringContextHolder.getProperties("ip.local-parsing", false, Boolean.class); if (ipLocal) { /* * 此文件為獨享 ,不必關閉 */ String path = "ip2region/ip2region.db"; String name = "ip2region.db"; try { config = new DbConfig(); file = FileUtil.inputStreamToFile(new ClassPathResource(path).getInputStream(), name); } catch (Exception e) { e.printStackTrace(); // log.error(e.getMessage(), e); } } }); } /** * Describe: Request 客戶端地址(獲取ip地址) * * @return {@link String} * */ public static String getIp() { HttpServletRequest request = getRequest(); String ipAddress = request.getHeader("x-forwarded-for"); if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getHeader("WL-Proxy-Client-IP"); } if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) { ipAddress = request.getRemoteAddr(); if ("127.0.0.1".equals(ipAddress) || "0:0:0:0:0:0:0:1".equals(ipAddress)) { // 根據網卡取本機配置的IP try { ipAddress = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { e.printStackTrace(); } } } //對於通過多個代理的情況,第一個IP為客戶端真實IP,多個IP按照','分割 if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15 if (ipAddress.indexOf(",") > 0) { ipAddress = ipAddress.substring(0, ipAddress.indexOf(",")); } } return ipAddress; } /** * 根據ip獲取詳細地址 */ public static String getCityInfo(String ip) { if (ipLocal) { return getLocalCityInfo(ip); } else { return getHttpCityInfo(ip); } } /** * 根據ip獲取詳細地址 */ public static String getHttpCityInfo(String ip) { String api = String.format(IP_URL, ip); cn.hutool.json.JSONObject object = JSONUtil.parseObj(HttpUtil.get(api)); return object.get("addr", String.class); } /** * 根據ip獲取詳細地址 */ public static String getLocalCityInfo(String ip) { try { DataBlock dataBlock = new DbSearcher(config, file.getPath()) .binarySearch(ip); String region = dataBlock.getRegion(); String address = region.replace("0|", ""); char symbol = '|'; if (address.charAt(address.length() - 1) == symbol) { address = address.substring(0, address.length() - 1); } return address.equals(REGION) ? "內網IP" : address; } catch (Exception e) { // log.error(e.getMessage(), e); e.printStackTrace(); } return ""; } /** * 獲取 HttpServletRequest 對象 * * @return {@link HttpServletRequest} * */ private static HttpServletRequest getRequest(){ ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); return servletRequestAttributes.getRequest(); } /** * Request 請求方法(類型) * * @return {@link String} * */ public static String getMethod(){ return getRequest().getMethod(); } /** * Request 請求頭 * * @param name 名稱 * @return {@link String} * */ public static String getHeader(String name){ return getRequest().getHeader(name); } /** * Request Agent * * @return {@link String} * */ private static String getAgent(){ return getHeader("User-Agent"); } /** * Request 瀏覽器類型 * * @return {@link String} * */ public static String getBrowser(){ String browser = ""; String userAgent = getAgent(); if (userAgent.contains("Firefox")) browser = "火狐瀏覽器"; else if (userAgent.contains("Chrome")) browser = "谷歌瀏覽器"; else if (userAgent.contains("Trident")) browser = "IE 瀏覽器"; else browser = "你用啥瀏覽器"; UserAgent.ImmutableUserAgent parse = userAgentAnalyzer.parse(userAgent); String value = parse.get(UserAgent.AGENT_NAME_VERSION).getValue(); return browser + "(" + value + ")"; } /** * Request 訪問來源 ( 客戶端類型 ) * * @return {@link String} * */ public static String getSystem(){ String userAgent = getAgent(); if (getAgent().toLowerCase().contains("windows" )) return "Windows"; else if (userAgent.toLowerCase().contains("mac" )) return "Mac"; else if (userAgent.toLowerCase().contains("x11" )) return "Unix"; else if (userAgent.toLowerCase().contains("android" )) return "Android"; else if (userAgent.toLowerCase().contains("iphone" )) return "IPhone"; else return "UnKnown, More-Info: " + userAgent; } }
2.4 aspect包(核心)
Log 實現 Aop 切面類
package com.example.log.aspect; import cn.hutool.json.JSONUtil; import com.example.log.annotation.Log; import com.example.log.common.context.BaseContext; import com.example.log.common.enums.Action; import com.example.log.common.utils.ExceptionUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Log 實現 Aop 切面類 */ @Aspect @Component public class LogAspect { ThreadLocal<Long> currentTime = new ThreadLocal<>(); /** * 基 礎 上 下 文 */ @Resource private BaseContext context; /** * 配置切入點(切 面 編 程) */ @Pointcut("@annotation(com.example.log.annotation.Log) || @within(com.example.log.annotation.Log)") public void logPointcut() { // 該方法無方法體,主要為了讓同類中其他方法使用此切入點 } /** * 處 理 系 統 日 志(配置環繞通知,使用在方法logPointcut()上注冊的切入點) * * @param joinPoint join point for advice */ @Around("logPointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { Object result = null; // 記錄方法的執行時間 currentTime.set(System.currentTimeMillis()); // 執 行 方 法 result = joinPoint.proceed(); // 注解解析 Log annotation = getAnnotation(joinPoint); String title = annotation.title(); Action action = annotation.action(); String describe = annotation.describe(); // 獲取方法名 String methodName = getMethodName(joinPoint); // 獲取參數 String parameter = getParameterToJson((ProceedingJoinPoint) joinPoint); // 請求耗時 Long time = System.currentTimeMillis() - currentTime.get(); currentTime.remove(); // 記 錄 日 志 context.record(title, methodName, parameter, action, true, time, null); return result; } /** * 配置異常通知 * * @param joinPoint join point for advice * @param e exception */ @AfterThrowing(pointcut = "logPointcut()", throwing = "e") public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { // 注解解析 Log annotation = getAnnotation((ProceedingJoinPoint) joinPoint); String title = annotation.title(); Action action = annotation.action(); String describe = annotation.describe(); // 獲取方法名 String methodName = getMethodName((ProceedingJoinPoint) joinPoint); // 獲取參數 String parameter = getParameterToJson((ProceedingJoinPoint) joinPoint); // 請求耗時 Long time = System.currentTimeMillis() - currentTime.get(); currentTime.remove(); // 異常詳細 byte[] exceptionDetail = ExceptionUtil.getStackTrace(e).getBytes(); // 記 錄 日 志 context.record(title, methodName, parameter, action, false, time, exceptionDetail); } /** * 獲 取 注 解 */ public Log getAnnotation(ProceedingJoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); Class<? extends Object> targetClass = point.getTarget().getClass(); Log targetLog = targetClass.getAnnotation(Log.class); if (targetLog != null) { return targetLog; } else { Method method = signature.getMethod(); Log log = method.getAnnotation(Log.class); return log; } } /** * 獲 取 方法名 */ public String getMethodName(ProceedingJoinPoint point) { MethodSignature signature = (MethodSignature) point.getSignature(); // 方法路徑 String methodName = point.getTarget().getClass().getName()+"."+signature.getName()+"()"; return methodName; } /** * 獲 取 參數(轉換json格式) */ public String getParameterToJson(ProceedingJoinPoint point) { List<Object> argList = new ArrayList<>(); //參數值 Object[] argValues = point.getArgs(); //參數名稱 String[] argNames = ((MethodSignature)point.getSignature()).getParameterNames(); if(argValues != null){ for (int i = 0; i < argValues.length; i++) { Map<String, Object> map = new HashMap<>(); String key = argNames[i]; map.put(key, argValues[i]); argList.add(map); map = null; } } if (argList.size() == 0) { return ""; } return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : JSONUtil.toJsonStr(argList); } }
2.5 三層架構
2.5.1 controller
package com.example.log.controller; import cn.hutool.json.JSON; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.example.log.annotation.Log; import com.example.log.service.LogService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequiredArgsConstructor @RequestMapping("/api/logs") @Api(tags = "日志管理") public class LogController { /** 使用@RequiredArgsConstructor 基於構造函數注入*/ private final LogService logService; /** * 查詢日志列表 */ @GetMapping("list") @ApiOperation(value = "查詢日志") public String list(){ return JSONUtil.parse(logService.list()).toString(); } /** * 測試 */ @GetMapping("demo") @Log(title = "測試") @ApiOperation(value = "測試") public String demo(@RequestParam String param){ return param; } }
2.5.2 service
package com.example.log.service; import com.baomidou.mybatisplus.extension.service.IService; import com.example.log.domain.SysLog; public interface LogService extends IService<SysLog> { }
package com.example.log.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.log.domain.SysLog; import com.example.log.repository.LogRepository; import com.example.log.service.LogService; import org.springframework.stereotype.Service; @Service public class LogServiceImpl extends ServiceImpl<LogRepository, SysLog> implements LogService { }
2.5.2 repository
package com.example.log.repository; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.log.domain.SysLog; import org.apache.ibatis.annotations.Mapper; @Mapper public interface LogRepository extends BaseMapper<SysLog> { }
3、測試
新增測試
http://127.0.0.1:8080//api/logs/demo?param=123
查看數據
http://127.0.0.1:8080//api/logs/list