介紹:接下來我會把學習階段學到的架構等知識點進行整合,每一次整合是在前一章的基礎上進行的,是以後面的整合不會重複放前面的代碼。每次的demo我放在結尾,本次是接着上一章的内容延續的,隻增加新增的或者修改的代碼。
上一章進行了Redis的整合,實作了一個使用者對應一個token,使用者登入失敗次數鎖定。
本章将整合AOP,實作請求接口後,對日志進行處理,不直接寫到邏輯裡面,盡量解耦,采用切面方式。
首先展示下我的目錄結構:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiNx8FesU2cfdGLwczX0xiRGZkRGZ0Xy9GbvNGLwIzXlpXazxSP9E0ZatUdlB1QiVTQClGVF5UMR9Fd4VGdsATNfd3bkFGazxycykFaKdkYzZUbapXNXlleSdVY2pESa9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5MjM2IDNxYTM3ATOwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
勾選部分為本章整合後新增部分代碼。
第一步:新增日志實體類
@Data
@TableName(value = "syslog")
@Accessors(chain = true)
public class SysLog {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.UUID)
private String id;//id
@TableField("operationUser")
private String operationUser;//操作人
@TableField("path")
private String path;//請求路徑
@TableField("time")
private String time;//方法執行時間
@TableField("parameter")
private String parameter;//方法入參
@TableField("title")
private String title;//操作方法
@TableField("action")
private String action;//方法描述
@TableField("sysType")
private Integer sysType;//系統類型
@TableField("opType")
private Integer opType;//操作類型
public SysLog(String operationUser, String path, String time,
String parameter, String title, String action, Integer sysType, Integer opType) {
super();
this.operationUser = operationUser;
this.path = path;
this.time = time;
this.parameter = parameter;
this.title = title;
this.action = action;
this.sysType = sysType;
this.opType = opType;
}
}
ps:此處所涉及的注解請不要漏寫,關系到mybatis-plus的使用,此處id的自動設定不一定使用 IdType.UUID。
可以采用前面整合的代碼生成器進行生成,我在此處就是使用代碼生成器生成的,但是需要改進一些地方,不然會報錯。
第二步:新增各層代碼
第三步:編寫切面需要依賴的注解(次重點)
【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝
ps:自定義注解,需要加上框選部分的注解,裡面參數的選擇在csdn上面有大佬有細講文章。
第四步:編寫切面(重點)
@Aspect
@Component
@EnableAsync
public class SystemLogAspect {
@Resource
private SyslogMapper logMapper;//日志 mapper
private String requestPath = null ; // 請求位址
private long startTimeMillis = 0; // 開始時間
private long endTimeMillis = 0; // 結束時間
private String user = null; // 操作人
private HttpServletRequest request = null;//請求
/**
* 注解的位置
*/
@Pointcut("@annotation(com.swagger.demo.config.OperationAnnotation)")
public void logPointCut() {}
/**
* @param joinPoint
* @Description 前置通知 方法調用前觸發 記錄開始時間,從session中擷取操作人
*/
@Before(value="logPointCut()")
public void before(JoinPoint joinPoint){
startTimeMillis = System.currentTimeMillis();
}
/**
* @param joinPoint
* @Description 擷取入參方法參數
* @return
*/
public Map<String, Object> getNameAndValue(JoinPoint joinPoint) {
Map<String, Object> param = new HashMap<>();
Object[] paramValues = joinPoint.getArgs();
String[] paramNames = ((CodeSignature)joinPoint.getSignature()).getParameterNames();
for (int i = 0; i < paramNames.length; i++) {
if(paramValues[i] instanceof Integer || paramValues[i] instanceof String) {
param.put(paramNames[i], paramValues[i]);
}
}
return param;
}
/**
* @param joinPoint
* @Description 後置通知 方法調用後觸發 記錄結束時間 ,操作人 ,入參等
*/
@After(value="logPointCut()")
public void after(JoinPoint joinPoint) {
request = getHttpServletRequest();
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class<?> targetClass = null;
try {
targetClass = Class.forName(targetName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method[] methods = targetClass.getMethods();
String title;
String action;
Integer sysType;
Integer opType;
Class<?>[] clazzs;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
clazzs = method.getParameterTypes();
if (clazzs!=null&&clazzs.length == arguments.length
&&method.getAnnotation(OperationAnnotation.class)!=null) {
request = getHttpServletRequest();
requestPath=request.getServletPath();
HttpSession session = request.getSession();
user = session.getAttribute("userName").toString();
title = method.getAnnotation(OperationAnnotation.class).content();
action = method.getAnnotation(OperationAnnotation.class).action();
sysType = method.getAnnotation(OperationAnnotation.class).sysType();
opType = method.getAnnotation(OperationAnnotation.class).opType();
endTimeMillis = System.currentTimeMillis();
SysLog log=new SysLog(user, requestPath,
(endTimeMillis-startTimeMillis)+"ms",
getNameAndValue(joinPoint).toString(), title, action,sysType,opType);
System.out.println("增加參數:"+log);
logMapper.insert(log);
// break;
}
}
}
}
/**
* @Description: 擷取request
*/
public HttpServletRequest getHttpServletRequest(){
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes)ra;
HttpServletRequest request = sra.getRequest();
return request;
}
/**
* @param joinPoint
* @return 環繞通知
* @throws Throwable
*/
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
return null;
}
/**
* @param joinPoint
* @Description 異常通知
*/
public void throwing(JoinPoint joinPoint) {
System.out.println("異常通知");
}
}
ps:下面對該切面進行一個解釋。
【八】springboot整合AOP實作日志操作(超詳細)碼字不易,若幫到各位,幫忙三連,感謝
aspect注解表示将該類定義為切面。
component注解表示将該類注冊到spring容器。
pointcut注解表示定義切入點,此處表示,切入到 OperationAnnotation注解的位置,定義切入點的參數特别多,可以去細細了解一下。
before注解表示前置通知,相當于BeforeAdvice的功能,value的值為切入點的名字。
此方法是擷取方法的入參。
after注解表示後置通知,即使切入點注解切入位置執行完之後執行該部分代碼。此處從session裡面擷取目前使用者,表示日志的操作人(在登入時将使用者名存入session),實際使用也可以采用其他辦法。
使用mybatis-plus的insert方法直接新增的記錄,也可以自己寫sql。
補充:改造了一下登入接口,登入成功後報錯了使用者名到session,如下:
ps:使用session需要在入參處加上HttpServletRequest request。
從登入的controller方法到實作類都需要加上該入參。
第五步:示範
請求登入接口
結果:
已經新增成功。
本期整合到此完畢,接下來會繼續更新加強整合,盡情期待。
通路位址:http://localhost:8088/swagger-ui.html或者http://localhost:8088/doc.html
demo位址:https://github.com/zrc11/studydemo/tree/main/%E6%95%B4%E5%90%88swagger