Spring AOP結(jié)合注解實(shí)現(xiàn)接口層操作日志記錄
1.表和實(shí)體設(shè)計(jì)
1.實(shí)體設(shè)計(jì)
實(shí)體基類
@Data
//映射將僅應(yīng)用于其子類
@MappedSuperclass
//指定要用于實(shí)體或映射超類的回調(diào)偵聽(tīng)器類。此注釋可以應(yīng)用于實(shí)體類或映射的超類。
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity implements Serializable {
/**
* @Id 注解標(biāo)識(shí)
* @GeneratedValue(generator = "snowflakeIdIDGenerator") 指定生成策略
* @GenericGenerator(name = "snowflakeIdIDGenerator", strategy = "net.cqnews.base.idconfig.SnowflakeIdGenerator")
* name 唯一的生成器名稱
* strategy 生成器策略可以是預(yù)定義的 Hibernate 策略或完全限定的類名。
* parameters 可選的生成器參數(shù)。
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(columnDefinition = "bigint(20) COMMENT '主鍵Id'")
private Long id;
@CreatedBy
@Column(columnDefinition = "bigint(20) COMMENT '創(chuàng)建人Id'")
private Long createId;
@CreatedDate
@Column(columnDefinition = "datetime COMMENT '創(chuàng)建時(shí)間'")
private Date createTime;
@LastModifiedBy
@Column(columnDefinition = "bigint(20) COMMENT '修改人Id'")
private Long updateId;
@LastModifiedDate
@Column(columnDefinition = "datetime COMMENT '修改時(shí)間'")
private Date updateTime;
/**
* 是否刪除
*/
@Column(columnDefinition = "tinyint default 0 COMMENT '是否刪除,默認(rèn)否'")
private Boolean deleted = Boolean.FALSE;
}操作日志表實(shí)體
@Entity
@Data
//指定表名
@Table(name = "sys_oper_log")
//表名注釋
@org.hibernate.annotations.Table(appliesTo = "sys_oper_log", comment = "系統(tǒng)操作日志表")
public class SysOperLog extends BaseEntity {
@Column(columnDefinition = "varchar(50) COMMENT '模塊標(biāo)題'")
private String title;
@Column(columnDefinition = "varchar(255) COMMENT '接口功能描述'")
private String description;
@Column(columnDefinition = "varchar(100) COMMENT '方法名稱'")
private String method;
@Column(columnDefinition = "varchar(10) COMMENT '請(qǐng)求方式'")
private String requestMethod;
@Column(columnDefinition = "varchar(50) COMMENT '操作人員名稱'")
private String operatorName;
@Column(columnDefinition = "varchar(255) COMMENT '請(qǐng)求URL'")
private String requestUrl;
@Column(columnDefinition = "varchar(255) COMMENT '主機(jī)地址'")
private String requestIp;
@Column(columnDefinition = "varchar(255) COMMENT '操作地點(diǎn)'")
private String operationLocation;
@Column(columnDefinition = "varchar(2000) COMMENT '請(qǐng)求參數(shù)'")
private String operatorParam;
@Column(columnDefinition = "varchar(2000) COMMENT '返回參數(shù)'")
private String jsonResult;
@Enumerated(EnumType.STRING)
@Column(columnDefinition = "varchar(20) COMMENT '操作狀態(tài)'")
private StatusEnum status;
@Column(columnDefinition = "varchar(2000) COMMENT '錯(cuò)誤描述'")
private String errorMsg;
}狀態(tài)枚舉類
@AllArgsConstructor
@Getter
public enum StatusEnum {
SUCCESS,
FAIL
}
2.表結(jié)構(gòu)設(shè)計(jì)
CREATE TABLE `sys_oper_log` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主鍵Id', `create_id` bigint DEFAULT NULL COMMENT '創(chuàng)建人Id', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間', `deleted` tinyint DEFAULT '0' COMMENT '是否刪除,默認(rèn)否', `update_id` bigint DEFAULT NULL COMMENT '修改人Id', `update_time` datetime DEFAULT NULL COMMENT '修改時(shí)間', `description` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '接口功能描述', `error_msg` varchar(2000) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '錯(cuò)誤描述', `json_result` varchar(2000) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '返回參數(shù)', `method` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '方法名稱', `operation_location` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作地點(diǎn)', `operator_name` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作人員名稱', `operator_param` varchar(2000) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '請(qǐng)求參數(shù)', `request_ip` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '主機(jī)地址', `request_method` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '請(qǐng)求方式', `request_url` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '請(qǐng)求URL', `status` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '操作狀態(tài)', `title` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '模塊標(biāo)題', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系統(tǒng)操作日志表';
2.日志注解
參考下面這段swagger接口文檔屬性的方式,在controller層日志記錄主要包含:接口所屬模塊、接口功能描述、請(qǐng)求參數(shù)、響應(yīng)參數(shù)等
當(dāng)然請(qǐng)求參數(shù)和響應(yīng)參數(shù)不是每個(gè)接口都會(huì)包含的,因此這里注解設(shè)計(jì)了isSaveRequestData是否保存請(qǐng)求參數(shù)、isSaveResponseData是否保存響應(yīng)的參數(shù)兩個(gè)字段便于靈活控制,當(dāng)然我們?cè)谧鋈罩居涗浱幚頃r(shí)也需要考慮為空的情況。
@Log(title = "測(cè)試模塊",description = "更新配置")
@ApiOperation(value = "更新配置")
@RequestMapping(value = "/update", method = RequestMethod.PUT)
public BaseResult update(@Validated @RequestBody AuthConfigUpdateRequest request) {
return BaseResult.judgeOperate(authConfigService.update(request));
}
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 接口所屬模塊
*/
public String title() default "";
/**
* 接口功能描述
*/
public String description() default "";
/**
* 是否保存請(qǐng)求的參數(shù)
*/
public boolean isSaveRequestData() default true;
/**
* 是否保存響應(yīng)的參數(shù)
*/
public boolean isSaveResponseData() default true;
}
3.核心AOP類
我們?cè)O(shè)計(jì)的@Log注解主要用于修飾controller層,MyLogAspect主要就是對(duì)被@Log修飾的目標(biāo)方法做增強(qiáng)處理,包含處理完請(qǐng)求后執(zhí)行:記錄目標(biāo)方法正在執(zhí)行的日志、處理請(qǐng)求異常后執(zhí)行:記錄目標(biāo)方法執(zhí)行錯(cuò)誤的日志
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class MyLogAspect {
private final SysOperLogService sysOperLogService;
/**
* 處理完請(qǐng)求后執(zhí)行
*
* @param joinPoint 切入點(diǎn)對(duì)象
* @param controllerLog @Log注解對(duì)象
* @param jsonResult 返回值對(duì)象
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
handleLog(joinPoint, controllerLog, jsonResult, null);
}
/**
* 處理請(qǐng)求異常后執(zhí)行
*
* @param joinPoint
* @param controllerLog
* @param e
*/
@AfterThrowing(pointcut = "@annotation(controllerLog)", throwing = "e")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Throwable e) {
handleLog(joinPoint, controllerLog, null, e);
}
/**
* 記錄日志
*
* @param joinPoint
* @param controllerLog
* @param e
* @param jsonResult
*/
private void handleLog(JoinPoint joinPoint, Log controllerLog, Object jsonResult, Throwable e) {
try {
//獲取HttpServletRequest對(duì)象
HttpServletRequest request = getRequest();
//獲取ip地址
String requestIp = IpUtils.getIpAddr(request);
//獲取請(qǐng)求地址
String requestUrl = request.getRequestURI();
//獲取類全名
String classFullName = joinPoint.getTarget().getClass().getName();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//方法名稱
String methodName = methodSignature.getMethod().getName();
//POST、GET、PUT、DELETE
String requestMethod = request.getMethod();
String operationLocation = AddressUtils.getRealAddressByIP(requestIp);
//保存日志
SysOperLog sysOperLog = new SysOperLog();
sysOperLog.setStatus(StatusEnum.SUCCESS);
sysOperLog.setOperationLocation(operationLocation);
sysOperLog.setRequestIp(requestIp);
sysOperLog.setRequestUrl(requestUrl);
sysOperLog.setMethod(classFullName + "." + methodName);
sysOperLog.setRequestMethod(requestMethod);
//獲取注解中對(duì)方法的描述信息 用于Controller層注解 請(qǐng)求參數(shù)、響應(yīng)參數(shù)設(shè)置
getControllerMethodDescription(joinPoint, controllerLog, sysOperLog, jsonResult);
if (e != null) {
//操作狀態(tài)(0正常 1異常)
sysOperLog.setStatus(StatusEnum.FAIL);
sysOperLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
}
sysOperLogService.saveOperatorLog(sysOperLog);
} catch (Exception err) {
if (e != null) {
log.error("方法執(zhí)行后異常通知,執(zhí)行錯(cuò)誤:{}", err.getMessage());
} else {
log.error("方法執(zhí)行成功返回通知,執(zhí)行錯(cuò)誤:{}", err.getMessage());
}
}
}
/**
* 獲取注解中對(duì)方法的描述信息 用于Controller層注解
* 請(qǐng)求參數(shù)、響應(yīng)參數(shù)設(shè)置
*
* @param joinPoint
* @param controllerLog
* @param sysOperLog
* @param jsonResult
*/
private void getControllerMethodDescription(JoinPoint joinPoint, Log controllerLog, SysOperLog sysOperLog, Object jsonResult) {
//接口所屬模塊
String title = controllerLog.title();
//接口功能描述
String description = controllerLog.description();
sysOperLog.setDescription(description);
sysOperLog.setTitle(title);
//判斷是否需要保存請(qǐng)求參數(shù)
if (controllerLog.isSaveRequestData()) {
// 獲取參數(shù)的信息,傳入到數(shù)據(jù)庫(kù)中。
setRequestValue(joinPoint, sysOperLog);
}
//判斷是否需要保存響應(yīng)數(shù)據(jù)
if (controllerLog.isSaveResponseData() && !ObjectUtils.isEmpty(jsonResult)) {
sysOperLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
}
}
/**
* 保存請(qǐng)求參數(shù)
*
* @param joinPoint
* @param sysOperLog
*/
private void setRequestValue(JoinPoint joinPoint, SysOperLog sysOperLog) {
//獲取請(qǐng)求參數(shù)
String requestMethod = sysOperLog.getRequestMethod();
if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
String params = argsArrayToString(joinPoint.getArgs());
sysOperLog.setOperatorParam(StringUtils.substring(params, 0, 2000));
} else if (HttpMethod.GET.name().equals(requestMethod)) {
Map<?, ?> paramsMap = (Map<?, ?>) getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
log.info("paramsMap:{}", paramsMap);
Map<String, String[]> parameterMap = getRequest().getParameterMap();
sysOperLog.setOperatorParam(StringUtils.substring(JSON.toJSONString(parameterMap), 0, 2000));
} else {
Map<?, ?> paramsMap = (Map<?, ?>) getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
log.info("paramsMap:{}", paramsMap);
sysOperLog.setOperatorParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
}
}
/**
* 參數(shù)拼裝
*/
private String argsArrayToString(Object[] paramsArray) {
String params = "";
if (paramsArray != null && paramsArray.length > 0) {
for (Object o : paramsArray) {
if (!ObjectUtils.isEmpty(o) && !isFilterObject(o)) {
try {
Object jsonObj = JSON.toJSON(o);
params += jsonObj.toString() + " ";
} catch (Exception e) {
}
}
}
}
return params.trim();
}
/**
* 判斷是否需要過(guò)濾的對(duì)象。
*
* @param o 對(duì)象信息。
* @return 如果是需要過(guò)濾的對(duì)象,則返回true;否則返回false。
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) o;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) o;
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
/**
* 獲取request對(duì)象
*
* @return
*/
private HttpServletRequest getRequest() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) attributes;
HttpServletRequest request = servletRequestAttributes.getRequest();
return request;
}
}4.用到的工具類
獲取ip地址
/**
* 獲取IP方法
*
*/
public class IpUtils {
/**
* 獲取客戶端IP
*
* @param request 請(qǐng)求對(duì)象
* @return IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip);
}
/**
* 檢查是否為內(nèi)部IP地址
*
* @param ip IP地址
* @return 結(jié)果
*/
public static boolean internalIp(String ip) {
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
/**
* 檢查是否為內(nèi)部IP地址
*
* @param addr byte地址
* @return 結(jié)果
*/
private static boolean internalIp(byte[] addr) {
if (addr == null || addr.length < 2) {
return true;
}
final byte b0 = addr[0];
final byte b1 = addr[1];
// 10.x.x.x/8
final byte SECTION_1 = 0x0A;
// 172.16.x.x/12
final byte SECTION_2 = (byte) 0xAC;
final byte SECTION_3 = (byte) 0x10;
final byte SECTION_4 = (byte) 0x1F;
// 192.168.x.x/16
final byte SECTION_5 = (byte) 0xC0;
final byte SECTION_6 = (byte) 0xA8;
switch (b0) {
case SECTION_1:
return true;
case SECTION_2:
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
return true;
}
case SECTION_5:
switch (b1) {
case SECTION_6:
return true;
}
default:
return false;
}
}
/**
* 將IPv4地址轉(zhuǎn)換成字節(jié)
*
* @param text IPv4地址
* @return byte 字節(jié)
*/
public static byte[] textToNumericFormatV4(String text) {
if (text.length() == 0) {
return null;
}
byte[] bytes = new byte[4];
String[] elements = text.split("\\.", -1);
try {
long l;
int i;
switch (elements.length) {
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L)) {
return null;
}
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L)) {
return null;
}
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 3:
for (i = 0; i < 2; ++i) {
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L)) {
return null;
}
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 4:
for (i = 0; i < 4; ++i) {
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)) {
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
break;
default:
return null;
}
} catch (NumberFormatException e) {
return null;
}
return bytes;
}
/**
* 獲取IP地址
*
* @return 本地IP地址
*/
public static String getHostIp() {
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
}
return "127.0.0.1";
}
/**
* 獲取主機(jī)名
*
* @return 本地主機(jī)名
*/
public static String getHostName() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
}
return "未知";
}
/**
* 從多級(jí)反向代理中獲得第一個(gè)非unknown IP地址
*
* @param ip 獲得的IP地址
* @return 第一個(gè)非unknown IP地址
*/
public static String getMultistageReverseProxyIp(String ip) {
// 多級(jí)反向代理檢測(cè)
if (ip != null && ip.indexOf(",") > 0) {
final String[] ips = ip.trim().split(",");
for (String subIp : ips) {
if (false == isUnknown(subIp)) {
ip = subIp;
break;
}
}
}
return ip;
}
/**
* 檢測(cè)給定字符串是否為未知,多用于檢測(cè)HTTP請(qǐng)求相關(guān)
*
* @param checkString 被檢測(cè)的字符串
* @return 是否未知
*/
public static boolean isUnknown(String checkString) {
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
}
public static void main(String[] args) {
String hostIp = IpUtils.getHostIp();
System.out.println(hostIp);
}
}根據(jù)ip地址獲取位置信息
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
/**
* 根據(jù)ip獲取地理位置信息
*/
public class AddressUtils {
private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
// IP地址查詢,太平洋ip地址查詢
public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
// 未知地址
public static final String UNKNOWN = "XX XX";
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* GBK 字符集
*/
public static final String GBK = "GBK";
public static String getRealAddressByIP(String ip) {
// 內(nèi)網(wǎng)不查詢
if (IpUtils.internalIp(ip)) {
return "內(nèi)網(wǎng)IP";
}
try {
String rspStr = sendGet(IP_URL, "ip=" + ip + "&json=true", GBK);
if (StringUtils.isNotBlank(rspStr)) {
log.error("獲取地理位置異常 {}", ip);
return UNKNOWN;
}
JSONObject obj = JSON.parseObject(rspStr);
String region = obj.getString("pro");
String city = obj.getString("city");
return String.format("%s %s", region, city);
} catch (Exception e) {
log.error("獲取地理位置異常 {}", ip);
}
return UNKNOWN;
}
/**
* 向指定 URL 發(fā)送GET方法的請(qǐng)求
*
* @param url 發(fā)送請(qǐng)求的 URL
* @param param 請(qǐng)求參數(shù),請(qǐng)求參數(shù)應(yīng)該是 name1=value1&name2=value2 的形式。
* @param contentType 編碼類型
* @return 所代表遠(yuǎn)程資源的響應(yīng)結(jié)果
*/
public static String sendGet(String url, String param, String contentType) {
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try {
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
log.info("sendGet - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
log.info("recv - {}", result);
} catch (ConnectException e) {
log.error("調(diào)用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("調(diào)用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
} catch (IOException e) {
log.error("調(diào)用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
} catch (Exception e) {
log.error("調(diào)用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception ex) {
log.error("調(diào)用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
}5.測(cè)試類
在需要記錄日志的目標(biāo)方法添加我們自定義的@Log注解,通過(guò)aop記錄日志
@Api(tags = "測(cè)試模塊")
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/test")
public class AuthController {
private final AuthConfigService authConfigService;
@Log(title = "測(cè)試模塊",description = "更新配置")
@ApiOperation(value = "更新配置")
@RequestMapping(value = "/update", method = RequestMethod.PUT)
public BaseResult update(@Validated @RequestBody AuthConfigUpdateRequest request) {
return BaseResult.judgeOperate(authConfigService.update(request));
}
@Log(title = "測(cè)試模塊",description = "新增配置")
@ApiOperation(value = "新增配置")
@RequestMapping(value = "/create", method = RequestMethod.POST)
public BaseResult create(@Validated @RequestBody AuthConfigRequest request) {
return BaseResult.judgeOperate(authConfigService.create(request));
}
@Log(title = "測(cè)試模塊",description = "刪除配置")
@ApiOperation(value = "刪除配置")
@DeleteMapping(value = "/delete/{ids}")
@ApiImplicitParams({
@ApiImplicitParam(name = "ids", value = "配置id,多個(gè)id用逗號(hào)分隔", required = true)
})
public BaseResult delete(@PathVariable("ids") List<Long> ids) {
return BaseResult.judgeOperate(authConfigService.delete(ids));
}
@Log(title = "測(cè)試模塊",description = "配置分頁(yè)")
@ApiOperation(value = "配置分頁(yè)")
@RequestMapping(value = "/list", method = RequestMethod.GET)
public BaseResult<PageResponse<AuthConfigListResponse>> list(@RequestBody AuthConfigListRequest request) {
return BaseResult.success(PageResponse.getInstance(authConfigService.list(request)));
}
@Log(title = "測(cè)試模塊",description = "根據(jù)appId獲取配置信息")
@ApiOperation(value = "根據(jù)appId獲取配置信息")
@RequestMapping(value = "/detail", method = RequestMethod.GET)
public BaseResult<EduAuthConfig> detail(String appId) {
return BaseResult.success(authConfigService.getAuthConfig(appId));
}
}6.測(cè)試結(jié)果

到此這篇關(guān)于Spring AOP結(jié)合注解實(shí)現(xiàn)接口層操作日志記錄的文章就介紹到這了,更多相關(guān)Spring AOP接口層內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot工程搭建打包、啟動(dòng)jar包和war包的教程圖文詳解
這篇文章主要介紹了SpringBoot工程搭建打包、啟動(dòng)jar包和war包的教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
java實(shí)現(xiàn)給第三方接口推送加密數(shù)據(jù)
這篇文章主要介紹了java實(shí)現(xiàn)給第三方接口推送加密數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
java List去掉重復(fù)元素的幾種方式(小結(jié))
這篇文章主要介紹了java List去掉重復(fù)元素的幾種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
mybatis中使用not?in與?in的寫(xiě)法說(shuō)明
這篇文章主要介紹了mybatis中使用not?in與?in的寫(xiě)法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Java面試為何阿里強(qiáng)制要求不在foreach里執(zhí)行刪除操作
那天,小二去阿里面試,面試官老王一上來(lái)就甩給了他一道面試題:為什么阿里的 Java 開(kāi)發(fā)手冊(cè)里會(huì)強(qiáng)制不要在 foreach 里進(jìn)行元素的刪除操作2021-11-11
springboot解決前后端分離時(shí)的跨域問(wèn)題
這篇文章主要介紹了springboot如何解決前后端分離時(shí)的跨域問(wèn)題,幫助大家更好的理解和學(xué)習(xí)使用springboot,感興趣的朋友可以了解下2021-04-04
深入Parquet文件格式設(shè)計(jì)原理及實(shí)現(xiàn)細(xì)節(jié)
這篇文章主要介紹了深入Parquet文件格式設(shè)計(jì)原理及實(shí)現(xiàn)細(xì)節(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08

