Aop实现注解日志的记录
日志实体 @Data public class ServiceLog implements Serializable { /** * 创建时间 */ private String createTime; /** * 方法名 */ private String method; /** * 参数 */ private String params; /** * 日志描述 */ private String description; /** * 方法运行时间 */ private Long runTime; /** * 方法返回值 */ private String response; } 注解类: @Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface ServiceInvoker { /** * 方法描述,可使用占位符获取参数:{{tel}} */ String service() default ""; } aop类: @Slf4j @Aspect @Component public class ServiceInvokerLog { /** * 环绕增强,相当于MethodInterceptor */ @Around(value = "@annotation(com.htsc.kh.extend.annotation.ServiceInvoker)") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { Object result = null; long time = System.currentTimeMillis(); try { result = joinPoint.proceed(); time = System.currentTimeMillis() - time; return result; } finally { try { //方法执行完成后增加日志 addOperationLog(joinPoint, result, time); } catch (Exception e) { throw e; } } } private void addOperationLog(JoinPoint joinPoint, Object res, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); ServiceLog operationLog = new ServiceLog(); operationLog.setRunTime(time); operationLog.setResponse(JSON.toJSONString(res)); operationLog.setParams(JSON.toJSONString(joinPoint.getArgs())); operationLog.setCreateTime(Util.formatDetailDate2(new Date())); operationLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName()); ServiceInvoker annotation = signature.getMethod().getAnnotation(ServiceInvoker.class); if (annotation != null) { operationLog.setDescription(getDetail(((MethodSignature) joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs(), annotation)); } //TODO 这里保存日志 log.warn("【"+operationLog+"】"); } /** * 对当前登录用户和占位符处理 * * @param argNames 方法参数名称数组 * @param args 方法参数数组 * @param annotation 注解信息 * @return 返回处理后的描述 */ private String getDetail(String[] argNames, Object[] args, ServiceInvoker annotation) { Map<Object, Object> map = new HashMap<>(4); for (int i = 0; i < argNames.length; i++) { map.put(argNames[i], args[i]); } String detail = annotation.service(); try { detail = "'" + "MethodName" + "'===>" + annotation.service(); for (Map.Entry<Object, Object> entry : map.entrySet()) { Object k = entry.getKey(); Object v = entry.getValue(); detail = detail.replace("{{" + k + "}}", JSON.toJSONString(v)); } } catch (Exception e) { throw e; } return detail; } }
实现效果:
2019-09-24 16:03:40,093 [ WARN] c.h.k.e.annotation.ServiceInvokerLog - 【ServiceLog(createTime=2020-09-24 16:03:40, method=com.hanwe.aop.InvokerService.register, params=[{"cId":"*******************","clientIp":"127.0.0.1"}], description='MethodName'===>register, runTime=39, response={"code":-500,"message":"'cid' field is illegal"})】
提示:如果是类中方法中的方法,需要单独把这个子方法拿出来建一个通用的类来管理他们。
2.补充
package com.dahan.annoation; import java.lang.annotation.*; /** * 日志类注解 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Documented public @interface LogAnnoation { String description(); String dateTime() default "19970101"; }
package com.dahan.aop; import com.dahan.annoation.LogAnnoation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.Arrays; /** * 记录日志 * * @author csh */ @Aspect @Component public class LogAop { /** * 第⼀步:编写⼀个⽅法 * 第⼆步:在⽅法使⽤@Pointcut注解 * 第三步:给注解的value属性提供切⼊点表达式 * 细节: * 1.在引⽤切⼊点表达式时,必须是⽅法名+(),例如"pointcut()"。 * 2.在当前切⾯中使⽤,可以直接写⽅法名。在其他切⾯中使⽤必须是全限定⽅法名。 */ // @Pointcut("execution(* com.dahan.*.*(..))") 这种方式需要详细到方法 @Pointcut("@annotation(com.dahan.annoation.LogAnnoation)") public void pointcut() { } /*@Before("pointcut()") public void beforePrintLog(JoinPoint jp) { Object[] args = jp.getArgs(); System.out.println("前置通知:beforePrintLog,参数是:" + Arrays.toString(args)); } @AfterReturning(pointcut = "pointcut()", returning = "ret") public void afterReturningPrintLog(Object ret) { System.out.println("后置通知:afterReturningPrintLog,参数是:" + ret); } @AfterThrowing(value = "pointcut()", throwing = "tw") public void afterThrowingPrintLog(Throwable tw) { System.out.println("异常通知:afterThrowingPrintLog,异常是:" + tw); } @After("pointcut()") public void afterPrintLog() { System.out.println("最终通知:afterPrintLog"); }*/ /** * 环绕通知 * * @param pjp pjp * @return Object */ @Around("pointcut()") public Object aroundPrintLog(ProceedingJoinPoint pjp) { //定义返回值 Object result = null; try { //前置通知 System.out.println("前置通知"); //1.获取参数 Object[] args = pjp.getArgs(); MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); String methodName = method.getName(); System.out.println("methodName: " + methodName); LogAnnoation annotation = method.getAnnotation(LogAnnoation.class); String dateTime = annotation.dateTime(); String description = annotation.description(); System.out.println("dateTime: " + dateTime + "---description: " + description); //2.执⾏切⼊点⽅法 result = pjp.proceed(args); //后置通知 System.out.println("后置通知"); } catch (Throwable e) { //异常通知 System.out.println("异常通知"); e.printStackTrace(); } finally { //最终通知 System.out.println("最终通知"); } return result; } }
本文来自博客园,作者:土木转行的人才,转载请注明原文链接