springboot-自定义注解
参考: 1. 自定义注解+AOP Spring Boot AOP记录用户操作日志 git代码
3.1 请求信息存到session中了
6. AOP介绍
实现功能:1 记录用户操作日志

/** * 日志切面类 */ @Aspect @Component @Order(2) @Slf4j public class LogAspect { @Autowired LogService logService; @Pointcut("@annotation(ins.business.common.annotation.Log)") public void pointcut() { } @Around("pointcut()") public Object around(ProceedingJoinPoint point) { Object result = null; long beginTime = System.currentTimeMillis(); try { result = point.proceed(); } catch (Throwable e) { e.printStackTrace(); } long time = System.currentTimeMillis() - beginTime; saveLog(point, time); return result; } /** * @param * @param */ private void saveLog(ProceedingJoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); UtiLogVo utiLogVo = new UtiLogVo(); Log logAnnotation = method.getAnnotation(Log.class); if (logAnnotation != null) { utiLogVo.setOperation(logAnnotation.value()); } String className = joinPoint.getTarget().getClass().getName(); String methodName = signature.getName(); utiLogVo.setMethod(className + "." + methodName + "()"); Object[] args = joinPoint.getArgs(); LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paramNames = u.getParameterNames(method); if (args != null && paramNames != null) { String params = ""; for (int i = 0; i < args.length; i++) { params += " " + paramNames[i] + ": " + args[i]; } utiLogVo.setParams(params); } HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); PrpdUser prpdUser = (PrpdUser) request.getSession().getAttribute("userInfo"); //插入myql中 if (prpdUser != null) { utiLogVo.setUserCode(prpdUser.getUserCode()); utiLogVo.setUserName(prpdUser.getUserName()); utiLogVo.setIp(IPUtils.getIpAddr(request)); utiLogVo.setTime(time); utiLogVo.setCreateTime(DateUtil.DateToString(new Date(), DateStyle.YYYY_MM_DD_HH_MM_SS)); utiLogVo.setUpdateTime(DateUtil.DateToString(new Date(), DateStyle.YYYY_MM_DD_HH_MM_SS)); utiLogVo.setLocation(IPUtils.getIpAddr(request)); utiLogVo.setId(GenerateCodeUtil.generateUUID()); logService.save(utiLogVo); } } }
2.实现接口访问的统一日志记录

package com.icbc.sd.config; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.logging.log4j.core.config.Order; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.google.gson.Gson; /** * * @ClassName: LogAspect * @Description: 日志记录AOP实现 * @author zln * */ @Aspect @Component @Order(1) public class LogAspect { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private String requestPath = null ; // 请求地址 private Map<?,?> inputParamMap = null ; // 传入参数 private Map<String, Object> outputParamMap = null; // 存放输出结果 private long startTimeMillis = 0; // 开始时间 private long endTimeMillis = 0; // 结束时间 /** * * @Title:doBeforeInServiceLayer * @Description: 方法调用前触发 * 记录开始时间 * @author zln * @param joinPoint */ @Before("execution(* com.icbc.sd.controller..*.*(..))") public void doBeforeInServiceLayer(JoinPoint joinPoint) { startTimeMillis = System.currentTimeMillis(); // 记录方法开始执行的时间 } /** * * @Title:doAfterInServiceLayer * @Description: 方法调用后触发 * 记录结束时间 * @author zln * @param joinPoint */ @After("execution(* com.icbc.sd.controller..*.*(..))") public void doAfterInServiceLayer(JoinPoint joinPoint) { endTimeMillis = System.currentTimeMillis(); // 记录方法执行完成的时间 this.printOptLog(); } /** * * @Title:doAround * @Description: 环绕触发 * @author zln * @param pjp * @return * @throws Throwable */ @Around("execution(* com.icbc.sd.controller..*.*(..))") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { /** * 1.获取request信息 */ RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes)ra; HttpServletRequest request = sra.getRequest(); // 获取输入参数 inputParamMap = request.getParameterMap(); // 获取请求地址 requestPath = request.getRequestURI(); // 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行 outputParamMap = new HashMap<String, Object>(); Object result = pjp.proceed();// result的值就是被拦截方法的返回值 outputParamMap.put("result", result); return result; } /** * * @Title:printOptLog * @Description: 输出日志 */ private void printOptLog() { Gson gson = new Gson(); // 需要用到google的gson解析包 String optTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTimeMillis); logger.info( "\n url:"+requestPath+";\n op_time:" + optTime + "\n pro_time:" + (endTimeMillis - startTimeMillis) + "ms ;" +"\n param:"+gson.toJson(inputParamMap)+";"+"\n result:"+gson.toJson(outputParamMap)); } }
<!-- aop依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
1.关于自定义注解 自定义注解的作用:用于标注需要监控的方法。Annotation(注解)
1.1 @Target 说明了Annotation所修饰的对象范围::被描述的注解可以用在什么地方
1.1.1 用在方法 @Target(ElementType.METHOD)
@Target 说明了Annotation所修饰的对象范围,取值(ElementType)有: 1.CONSTRUCTOR:用于描述构造器 2.FIELD:用于描述域 3.LOCAL_VARIABLE:用于描述局部变量 4.METHOD:用于描述方法 5.PACKAGE:用于描述包 6.PARAMETER:用于描述参数 7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
1.2.1 如果需要运行时区动态获取注解信息,只能用 @Retention(RetentionPolicy.RUNTIME)
1.SOURCE:在源文件中有效(即源文件保留) 2.CLASS:在class文件中有效(即class保留) 3.RUNTIME:在运行时有效(即运行时保留)
1.3 定义一个方法级别的@Log注解,用于标注需要监控的方法
/** * @author Lenovo */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String value() default ""; }
2.AOP ,定义一个LogAspect类,使用@Aspect
标注让其成为一个切面,切点为使用@Log
注解标注的方法,使用@Around
环绕通知:
2.1
JointPoint 和 ProceedingJoinPoint 的使用区别
环绕通知(执行顺序)=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的
2.6 关于切入点表达式的形式
形式1:@annotation(包名.注解名) 按注解进行拦截.
@Pointcut("@annotation(com.tsbx.common.annotation.Log)")
形式2:execution 表达式 和 within 表达式区别
@Pointcut("execution(* com.icbc.sd.controller..*.*(..))") 的解释
1. 第一个*号:返回值类型,*号表示所有的类型,即通配符。
2. 包名:需要拦截的包,
3. .. 两个点表示当前包和当前包的所有子包,即例子中的com.icbc.sd.controller包和该包的子孙包下所有类
4. 第二个*号:类名,*号表示所有的类。
5. *(..):方法名,*号表示所有方法,括号里面表示方法的参数,两个点表示任何参数,可有可无。
3.spring-boot使用AOP统一处理日志 2
试一下post方式 @RequestBody json(map)格式数据 是否能拿到请求参数 可以
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升