SpringAOP+自定义注解实现日志记录
关于自定义注解基本介绍可参考以往博客:https://www.cnblogs.com/DFX339/p/11386722.html
此文主要是讲述如何通过注解标识记录日志信息,一般我们的Service接口都需要记录入参信息,参数校验,方法执行时间等
处理思路:
1.自定义注解,使用的时候通过加入注解即可注入相应的日志信息
2.使用SpringAOP, 识别注解标识的方法,切入日志信息
步骤一: 定义注解类 SystemLog.java
package com.im.nexus.service.utils.logUtils; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @description:系统日志注解(使用在Service类里面的方法上) * @author: duanfeixia * @time: 2020/5/12 10:54 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface SystemLog { String methoddesc() default "";//方法描述 }
步骤二:定义切面 SystemLogAspect.java
这里切记需要加入 @Component @Aspect 这两个注解,@Slf4j 这个注解是日志类的注解 非必选
package com.im.nexus.service.utils.logUtils; import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; /** * 自定义日志注解 SystemLog配合AOP注入日志信息 */ @Component @Aspect @Slf4j public class SystemLogAspect { // @SystemLog标识为切入点,切面会根据@SystemLog注解来注入数据 @Pointcut("@annotation(com.imodule.nexus.service.utils.logUtils.SystemLog)") public void logPointCut(){ } //环绕注入 @Around("logPointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { long startTime = System.currentTimeMillis();//记录目标方法的开始执行时间 Object result = point.proceed();//执行目标方法 long methodexecutetime = System.currentTimeMillis() - startTime; //日志信息记录 try { MethodSignature methodSignature = (MethodSignature) point.getSignature(); Method method = methodSignature.getMethod(); SystemLog systemLog = method.getDeclaredAnnotation(SystemLog.class); String className = point.getTarget().getClass().getName();//类名 String methodName = method.getName();//方法名 //入参信息 Object[] args = point.getArgs(); List<String> list = new ArrayList<String>(); for (Object obj : args) { list.add(new Gson().toJson(obj)); } log.info(className+"."+methodName+","+systemLog.methoddesc()+",入参:"+list.toString()); log.info(className+"."+methodName+","+systemLog.methoddesc()+",耗时:"+methodexecutetime+"ms"); } catch (Exception e) { log.error("日志切入出错"); } return result; } }
步骤三: 在具体Service实现类的方法上使用 @SystemLog注解
使用示例:@SystemLog(methoddesc = "方法说明内容")
package com.im.nexus.service.impl.prd; import com.im.common.frame.exception.IMException; import com.im.nexus.constant.NexusExceptionCodeEnum; import com.im.nexus.dao.prd.PrdTsarateMapper; import com.im.nexus.service.utils.BizParamCheckUtils; import com.im.nexus.service.utils.ExceptionThrowUtils; import com.im.nexus.service.utils.logUtils.SystemLog; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @description 产品接口实现类 * @date 2020-03-31 * @author duanfeixia */ @Service @Slf4j public class prdServiceImpl implements prdService { @Autowired private PrdTsarateMapper prdTsarateMapper; @Override @SystemLog(methoddesc = "是否简易核保-注解标识") public PrdResultDTO isSimple(String prdCode) throws IMException { //入參對象校驗 BizParamCheckUtils.paramPropertyNullCheck("prdServiceImpl.getPrdStaticInfo:prdCode",prdCode); PrdResultDTO prdResultDTO= new PrdResultDTO(); try { Integer prdflag = prdTsarateMapper.isSimple(prdCode); /// 这里遵照了以前的写法,建议后续使用 1:是,0:否 if (null != prdflag && prdflag > 0) { // 屬於簡易核保 : 0 prdResultDTO.setIsSimple(SysCnst.STR_FLAG_NO); } else { // 不屬於簡易核保 : 1 prdResultDTO.setIsSimple(SysCnst.STR_FLAG_YES); } } catch (Exception e) { log.error("prdServiceImpl.isSimple exception:[prdcode=" + prdCode + "]"); ExceptionThrowUtils.logAndThrowException(NexusExceptionCodeEnum.EXCEPTION_DB_000001, e); } return prdResultDTO; } }
步骤四:定义测试类,对以上方法进行测试(Junit单元测试)
package com.im.nexus.service.prd; import com.im.common.frame.exception.IMException; import com.im.nexus.DTO.prd.*; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class prdServiceTest { @Autowired private prdService prdService; @Test public void testisSimple() throws IMException{ try { PrdResultDTO prdTsarateRtl = prdService.isSimple("12"); log.info("是否简易核保:"+prdTsarateRtl); log.info("prdTsarateRtl.getIsSimple:"+prdTsarateRtl.getIsSimple()); } catch (Exception e) { log.error("ceshi :", e); } } }
执行单元测试后,测试结果如下:
[] 2020-05-12 12:59:33:331 INFO [main] c.i.n.s.u.logUtils.SystemLogAspect 51 - com.im.nexus.service.impl.prd.prdServiceImpl.isSimple,是否简易核保-PRD,入参:["12"] [] 2020-05-12 12:59:33:331 INFO [main] c.i.n.s.u.logUtils.SystemLogAspect 52 - com.im.nexus.service.impl.prd.prdServiceImpl.isSimple,是否简易核保-PRD,耗时:66ms [] 2020-05-12 12:59:33:331 INFO [main] c.i.n.service.prd.prdServiceTest 26 - 是否简易核保:PrdTsarateRtl{isSimple=1} [] 2020-05-12 12:59:33:331 INFO [main] c.i.n.service.prd.prdServiceTest 27 - prdTsarateRtl.getIsSimple:1
前面两行则是我们通过@SystemLog注解注入的日志内容
后面两行则是我们在Service实现类的方法中手写的日志内容