SysOperateLog
先看注解的定義:
@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface SysOperateLog {
/**
* 操作類型,取值為 com.gosun.isap.operlog.api.OperateType
*
* @return 操作類型
*/
int operateType();
/**
* 業(yè)務(wù)類型傀蓉,取值為com.gosun.isap.operlog.api.ServiceType
*
* @return 業(yè)務(wù)類型
*/
int serviceType();
/**
* 操作描述
*
* @return 操作描述信息
*/
String description();
}
一個目標(biāo)為方法的運行時注解欧漱,有三個數(shù)據(jù)。
Around
通過Spring Aop切面實現(xiàn)日志的記錄功能葬燎,在這里主要用到了Around這個注解误甚,該注解的意思是在目標(biāo)方法運行之前和運行之后增加某些功能缚甩,可以改變執(zhí)行目標(biāo)方法的參數(shù)值,也可以改變執(zhí)行目標(biāo)方法之后的返回值窑邦。
在我們的項目中:
@Around("within(com.gosun.isap..*) && @annotation(rl)")
如果看不懂肯定就是因為within(com.gosun.isap..*) && @annotation(rl)
這句表達(dá)式擅威。
下面就分析一下這句表達(dá)式的意思:
within的官方定義是:
within - limits matching to join points within certain types (simply the execution of a method declared within a matching type when using Spring AOP)
簡單的說就是匹配某個類型。
com.gosun.isap..*
:
官方例子:within(com.xyz.service..*)
any join point (method execution only in Spring AOP) within the service package or a sub-package
和spring的component-scan類似冈钦,遞歸掃描下面的包郊丛。
@annotation的官方定義是:
@annotation - limits matching to join points where the subject of the join point (method being executed in Spring AOP) has the given annotation
簡單的說就是只匹配擁有某個注解的方法。
上面兩個條件用&&連接起來意思就很清楚了瞧筛,切入com.gosun.isap
下的包厉熟,并且在類方法包含SysOperateLog注解的情況下,增加日志功能较幌。
Spring
在spring配置文件中添加配置啟用Aop功能
<context:component-scan base-package="com.gosun.isap.operlog"></context:component-scan>
<aop:aspectj-autoproxy/>
實現(xiàn)
在看看方法的具體實現(xiàn):
@Around("within(com.gosun.isap..*) && @annotation(rl)")
public Object writeOperateLog(ProceedingJoinPoint jp, SysOperateLog rl) throws Throwable {
String className = jp.getTarget().getClass().toString();// 獲取目標(biāo)類名
className = className.substring(className.indexOf("com"));
String signature = jp.getSignature().toString();// 獲取目標(biāo)方法簽名
String methodName = signature.substring(signature.lastIndexOf(".") + 1, signature.indexOf("("));
Object[] parames = jp.getArgs();// 獲取目標(biāo)方法體參數(shù)
String params = parseParames(parames); // 解析目標(biāo)方法體的參數(shù)
int serviceType = rl.serviceType();
int operateType = rl.operateType();
String description = rl.description();
StringBuilder sbLogDetail = new StringBuilder();
sbLogDetail.append(description).append(", 參數(shù)為:").append(params);
Object object;
try {
object = jp.proceed();
if (object != null && object instanceof ResponseResult) {
int code = ((ResponseResult) object).getHead().getErrorCode();
if (code == ErrorCode.ERR_OK) {
success(serviceType, operateType, sbLogDetail.toString());
} else {
failed(serviceType, operateType, sbLogDetail.toString(),
((ResponseResult) object).getHead().getMessage());
}
} else {
success(serviceType, operateType, sbLogDetail.toString());
}
} catch (Throwable throwable) {
failed(serviceType, operateType, sbLogDetail.toString(), throwable.getMessage());
throw throwable;
}
return object;
}
private void success(int serviceType, int operateType, String description) {
try {
OperateLogWriter.success(serviceType, operateType, description);
} catch (Exception e) {
logger.error("Write operate log error ", e);
}
}
private void failed(int serviceType, int operateType, String description, String reason) {
try {
OperateLogWriter.fail(serviceType, operateType, description, reason);
} catch (Exception e) {
logger.error("Write operate log error ", e);
}
}
可以看到不管是成功還是失敗揍瑟,最終依舊調(diào)用了OperateLogWriter去寫日志,但通過AOP的方式解耦了分散在業(yè)務(wù)邏輯中的寫日志代碼乍炉。
在這里object = jp.proceed();
才真正的執(zhí)行了我們的方法绢片,并通過返回的對象int code = ((ResponseResult) object).getHead().getErrorCode();
判斷方法的成功與失敗,然后寫入日志。
int serviceType = rl.serviceType();
int operateType = rl.operateType();
String description = rl.description();
這三句話將注解中的內(nèi)容提取出來寫入日志中,因此這個注解的使用也非常簡單:
@SysOperateLog(serviceType = ServiceType.CONFIG_RES裁良, operateType = OperateType.CONFIG_ADD, description = "添加時間模板")
最終這些元數(shù)據(jù)都會寫入到日志中扰付。