UOJ32【UR #2】跳蚤公路
阿新 • • 發佈:2020-12-29
基於aop思想實現日誌列印
基於切點表示式實現:
- execution詳解
execution的語法表示式如下:execution(<修飾符> <返回型別> <類路徑> <方法名>(<引數列表>) <異常模式> )
其中,修飾符和異常是可選的,如果不加類路徑,則預設對所有的類生效。它常用例項如下: - 通過方法簽名、返回值定義切點:
execution(public * *Service(..))
:定位於所有類下返回值任意、方法入參型別、數量任意,public型別的方法execution(public String *Service(..))
- 通過類包定義切點:
execution(* com.yc.controller.BaseController+.*(..))
:匹配任意返回型別,對應包下BaseController類及其子類等任意方法。execution(* com.*.(..))
:匹配任意返回型別,com包下所有類的所有方法execution(* com..*.(..))
:匹配任意返回型別,com包、子包下所有類的所有方法
注意.表示該包下所有類,…則涵括其子包。
- 通過方法入參定義切點
- 這裡“*”表示任意型別的一個引數,“…”表示任意型別任意數量的引數
execution(* speak(Integer,*))
:匹配任意返回型別,所有類中只有兩個入參,第一個入參為Integer,第二個入參任意的方法execution(* speak(..,Integer,..))
:匹配任意返回型別,所有類中至少有一個Integer入參,但位置任意的方法。
- 常用切點表示式
execution(* com.yc.service..*.*(..))
在配置service層的事務管理時常用,定位於任意返回型別(第一個”*”
) 在com.yc.service包以及(“..”
)子包下的(第一個“*”
)所有類(第二個”*”
)下的所有方法(第三個”*”
),且這個方法的入參為任意型別、數量(體現在“(..)“
/**
* Created by yangmin on 2020/10/26
*/
@Aspect
@Component
public class LoggerAop {
private static Logger log = LoggerFactory.getLogger(LoggerAop.class);
/**
* 此處的切點是註解的方式
* 只要出現 @LogAnnotation註解都會進入
*/
/**
* 此處的切點使用的是切點表示式
* execution(* com.yc.service.*.*(..))在配置service層的事務管理時常用,定位於任意返回型別(第一個”*”) 在com.yc.service包下的所有類(第二個”*”)下的所有方法(第三個”*”),且這個方法的入參為任意型別、數量(體現在 “(..)“)
*
*/
@Pointcut("execution(public * com.study.studyaop.service..*.*(..))")//切入點描述,這個是service包的切入點
public void logPointCut() {
}
/**
* 環繞增強,相當於MethodInterceptor
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("logPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("==============================================start==================================================");
Object result=null;
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//獲取類名
String className = joinPoint.getTarget().getClass().getName();
//獲取方法名
String methodName = signature.getName();
long beginTime = System.currentTimeMillis();
//獲取方法引數
Object[] args = joinPoint.getArgs();
//在引數中去除Request或者Response物件,(joinPoint.getArgs()返回的陣列中攜帶有Request或者Response物件,導致序列化異常。)
Stream<?> stream = ArrayUtils.isEmpty(args) ? Stream.empty() : Arrays.stream(args);
List<Object> logArgs = stream
.filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
.collect(Collectors.toList());
String params = null;
if (args.length != 0) {
params = JSON.toJSONString(logArgs);
}
HttpServletRequest request = HttpContextUtil.getRequest();
//獲取請求方法的型別:get 或則post
String method = request.getMethod();
//獲取請求路勁
String url = request.getRequestURL().toString();
//獲取ip
String ip = IpUtils.getIP(request);
log.info("請求的路徑為 : {}",url);
log.info("請求ip : {}",ip);
log.info("方法請求類名 : {}",className);
log.info("方法名 : {}",methodName);
log.info("方法型別 : {}",method);
log.info("請求引數 : {}",params);
//執行方法
try {
result = joinPoint.proceed();
} catch (Exception e) {
log.error("異常資訊 : {}",e.getMessage());
e.printStackTrace();
}finally {
//執行時長(毫秒)
long time = System.currentTimeMillis() - beginTime;
log.info("執行時間 : {}",time);
log.info("==============================================end==================================================");
return result;
}
}
/**
獲取request的方法
*/
public HttpServletRequest getRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
}
/**
* 獲取IP地址的方法
* @return
*/
public String getIpAddress() {
HttpServletRequest request = getRequest();
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("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
基於註解實現
annotation 此註解用於定位標註了某個註解的目標切點。下面我們來看一個模擬使用者登入成功後日志記錄的示例
1.自定義註解
/**
* 日誌註解
*
* @author lastwhisper
*/
@Target(ElementType.METHOD) // 方法註解
@Retention(RetentionPolicy.RUNTIME) // 執行時可見
public @interface LogAnno {
String operateType();// 記錄日誌的操作型別
int type();
}
- 目標方法
/**
*登入方法
* @param request
* @return
* @throws Exception
*/
@LogAnno(operateType = "登入",type=10)
@RequestMapping(value = "/tologin", method = RequestMethod.POST)
public String login(@RequestBody Map<String,String> map , HttpServletRequest request, HttpServletResponse response) throws Exception {
String username = map.get("username");
String password = map.get("password");
String code = map.get("code");
LoginResult loginResult = loginService.login(code,username, password,request,response);
String s = JsonUtils.toString(loginResult);
return s;
}
3.增強
@Order(3)//優先順序
@Component
@Aspect
public class LogAopAspect {
@Resource
private PermissApiClient permissApiClient;
@Around("@annotation(com.woyaoce.core.annotation.LogAnno)")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
// 1.方法執行前的處理,相當於前置通知
// 獲取方法簽名
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
// 獲取方法
Method method = methodSignature.getMethod();
// 獲取方法上面的註解
LogAnno logAnno = method.getAnnotation(LogAnno.class);
// 獲取操作描述的屬性值
String operateType = logAnno.operateType();
int type = logAnno.type();
// 建立一個日誌物件(準備記錄日誌)
CmsLog cmsLog = new CmsLog();
cmsLog.setType(type);
String ip = HttpContextUtil.getIpAddress();
cmsLog.setIp(ip);
Object result = null;
try {
// 讓代理方法執行
result = pjp.proceed();
// 2.相當於後置通知(方法成功執行之後走這裡)
cmsLog.setStatus(0);
cmsLog.setLogMsg(operateType);// 設定操作結果
} catch (Exception e) {
// 3.相當於異常通知部分
cmsLog.setStatus(1);
cmsLog.setLogMsg(operateType);// 設定操作結果
} finally {
// 設定操作人,從session中獲取,
Subject LoginUser = SecurityUtils.getSubject();
Session session = LoginUser.getSession();
String userName =(String) session.getAttribute("userName");
cmsLog.setLoginName(userName);
// 4.相當於最終通知
//cmsLog.setCreateDate(new Date());// 設定操作日期
// 新增日誌記錄
permissApiClient.addLog(cmsLog);
}
return result;
}
}