一:功能简介

本文主要记录如何使用aop切面的方式来实现日志记录功能。

主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型(增删改查),详细描述,返回值。

二:项目结构图

三:代码实现

1.配置文件

spring.aop.auto=true #开启spring的aop配置,简单明了,不需要多配置其他的配置或注解。
server.port=1000  #端口号

2.AOP切点类

这个是最主要的类,可以使用自定义注解或针对包名实现AOP增强。

1)这里实现了对自定义注解的环绕增强切点,对使用了自定义注解的方法进行AOP切面处理;

2)对方法运行时间进行监控;

3)对方法名,参数名,参数值,对日志描述的优化处理;

在方法上增加@Aspect 注解声明切面,使用@Pointcut 注解定义切点,标记方法。

使用切点增强的时机注解:@Before,@Around,@AfterReturning,@AfterThrowing,@After

package com.example.aopdemo.config;import com.alibaba.fastjson.JSON;
import com.example.aopdemo.pojo.OperationLog;
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.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;/***@author lxy*@description 切面*@date 2020/12/16*/
@Aspect
@Component
public class LogAspect {/*** 此处的切点是注解的方式,也可以用包名的方式达到相同的效果* '@Pointcut("execution(* com.wwj.springboot.service.impl.*.*(..))")'*/@Pointcut("@annotation(com.example.aopdemo.config.OperationLogDetail)")public void operationLog(){}/*** 环绕增强,相当于MethodInterceptor*/@Around("operationLog()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {Object res = null;long time = System.currentTimeMillis();try {res =  joinPoint.proceed();time = System.currentTimeMillis() - time;return res;} finally {try {//方法执行完成后增加日志addOperationLog(joinPoint,res,time);}catch (Exception e){System.out.println("LogAspect 操作失败:" + e.getMessage());e.printStackTrace();}}}private void addOperationLog(JoinPoint joinPoint, Object res, long time){MethodSignature signature = (MethodSignature)joinPoint.getSignature();OperationLog operationLog = new OperationLog();operationLog.setRunTime(time);operationLog.setReturnValue(JSON.toJSONString(res));operationLog.setId(UUID.randomUUID().toString());operationLog.setArgs(JSON.toJSONString(joinPoint.getArgs()));operationLog.setCreateTime(new Date());operationLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName());operationLog.setUserId("#{currentUserId}");operationLog.setUserName("#{currentUserName}");OperationLogDetail annotation = signature.getMethod().getAnnotation(OperationLogDetail.class);if(annotation != null){operationLog.setLevel(annotation.level());operationLog.setDescribe(getDetail(((MethodSignature)joinPoint.getSignature()).getParameterNames(),joinPoint.getArgs(),annotation));operationLog.setOperationType(annotation.operationType().getValue());operationLog.setOperationUnit(annotation.operationUnit().getValue());}//TODO 这里保存日志System.out.println("记录日志:" + operationLog.toString());
//        operationLogService.insert(operationLog);}/*** 对当前登录用户和占位符处理* @param argNames 方法参数名称数组* @param args 方法参数数组* @param annotation 注解信息* @return 返回处理后的描述*/private String getDetail(String[] argNames, Object[] args, OperationLogDetail 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.detail();try {detail = "'" + "#{currentUserName}" + "'=》" + annotation.detail();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){e.printStackTrace();}return detail;}/*  @Before("operationLog()")public void doBeforeAdvice(JoinPoint joinPoint){System.out.println("进入方法前执行.....");}*//*** 处理完请求,返回内容* @param ret*//* @AfterReturning(returning = "ret", pointcut = "operationLog()")public void doAfterReturning(Object ret) {System.out.println("方法的返回值 : " + ret);}
*//*** 后置异常通知*//*@AfterThrowing("operationLog()")public void throwss(JoinPoint jp){System.out.println("方法异常时执行.....");}*//*** 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行*//*@After("operationLog()")public void after(JoinPoint jp){System.out.println("方法最后执行.....");}*/}

3.自定义注解

package com.example.aopdemo.config;import com.example.aopdemo.constant.OperationType;
import com.example.aopdemo.constant.OperationUnit;import java.lang.annotation.*;/*** Created by IntelliJ IDEA** @author lxy* @date 2018/9/12*/
//@OperationLogDetail(detail = "通过手机号[{{tel}}]获取用户名",level = 3,operationUnit = OperationUnit.USER,operationType = OperationType.SELECT)
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLogDetail {/*** 方法描述,可使用占位符获取参数:{{tel}}*/String detail() default "";/*** 日志等级:自己定,此处分为1-9*/int level() default 0;/*** 操作类型(enum):主要是select,insert,update,delete*/OperationType operationType() default OperationType.UNKNOWN;/*** 被操作的对象(此处使用enum):可以是任何对象,如表名(user),或者是工具(redis)*/OperationUnit operationUnit() default OperationUnit.UNKNOWN;
}

4.注解用到的枚举类型

package com.example.aopdemo.constant;public enum OperationType {/*** 操作类型*/UNKNOWN("unknown"),DELETE("delete"),SELECT("select"),UPDATE("update"),INSERT("insert");private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;}OperationType(String s) {this.value = s;}
}
package com.example.aopdemo.constant;public enum OperationUnit {/*** 被操作的单元*/UNKNOWN("unknown"),USER("user"),EMPLOYEE("employee"),Redis("redis");private String value;OperationUnit(String value) {this.value = value;}public String getValue() {return value;}public void setValue(String value) {this.value = value;}
}

5.日志记录对象

package com.example.aopdemo.pojo;import lombok.Data;import java.util.Date;/***@author liuxingying*@description*@date 2020/12/16*/
@Data
public class OperationLog {private String id;private Date createTime;/*** 日志等级*/private Integer level;/*** 被操作的对象*/private String operationUnit;/*** 方法名*/private String method;/*** 参数*/private String args;/*** 操作人id*/private String userId;/*** 操作人*/private String userName;/*** 日志描述*/private String describe;/*** 操作类型*/private String operationType;/*** 方法运行时间*/private Long runTime;/*** 方法返回值*/private String returnValue;
}

6.springboot启动类

package com.example.aopdemo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class AopdemoApplication {public static void main(String[] args) {SpringApplication.run(AopdemoApplication.class, args);}}

7.controller类

package com.example.aopdemo.controller;import com.example.aopdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;/***@author liuxingying*@description*@date 2020/12/16*/
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;/*** 访问路径 http://localhost:11000/user/findUserNameByTel?tel=1234567* @param tel 手机号* @return userName*/@RequestMapping("/findUserNameByTel")public String findUserNameByTel(@RequestParam("tel") String tel){return userService.findUserName(tel);}
}

8.Service接口 和 ServiceImpl实现类

package com.example.aopdemo.service;public interface UserService {/*** 获取用户信息* @return* @param tel*/String findUserName(String tel);
}
package com.example.aopdemo.service;import com.example.aopdemo.config.OperationLogDetail;
import com.example.aopdemo.constant.OperationType;
import com.example.aopdemo.constant.OperationUnit;
import org.springframework.stereotype.Service;/***@author lxy*@description*@date 2020/12/16*/
@Service
public class UserServiceImpl implements UserService {@OperationLogDetail(detail = "通过手机号[{{tel}}]获取用户名",level = 3,operationUnit = OperationUnit.USER,operationType = OperationType.SELECT)@Overridepublic String findUserName(String tel) {System.out.println("tel:" + tel);return "zhangsan";}
}

四:pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.1</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>aopdemo</artifactId><version>0.0.1-SNAPSHOT</version><name>aopdemo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- 切面 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.59</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build></project>

五:运行结果

进入方法前执行.....
tel:1234567
记录日志:OperationLog{id='cd4b5ba7-7580-4989-a75e-51703f0dfbfc', createTime=Fri Sep 14 08:54:55 CST 2018, level=3, operationUnit='user', method='com.wwj.springboot.service.impl.UserServiceImpl.findUserName', args='["1234567"]', userId='#{currentUserId}', userName='#{currentUserName}', describe=''#{currentUserName}'=》通过手机号["1234567"]获取用户名', operationType='select', runTime=4, returnValue='"zhangsan"'}
方法最后执行.....
方法的返回值 : zhangsan

参考自 俊俊的小熊饼干。

Aop切面自定义注解的使用相关推荐

  1. 如何使用 AOP 和自定义注解?

    作者 | 一个程序员的成长 责编 | 胡巍巍 记得今年年初刚开始面试的时候,被问的最多的就是你知道Spring的两大核心嘛?那你说说什么是AOP,什么是IOC?我相信你可能也被问了很多次了. 到底是什 ...

  2. AspectJ切面自定义注解实现参数分组校验——基础概念(2)

    AspectJ切面自定义注解实现参数分组校验--基础概念(2) 一.环境 二.创建AspectJ 2-1.基础概念 2-2.Pointcut规则表达式 2-3.切点标志符pointcut design ...

  3. 【Java注解系列】内置注解与AOP实现自定义注解

    Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制. Java 语言中的类.方法.变量.参数和包等都可以被标注.和 Javadoc 不同,Java 标注可 ...

  4. 自定义注解实现权限校验含义_厉害了!老大利用AOP实现自定义注解,半小时完成我三天工作量...

    前面我们已经介绍了AOP适用的一些业务场景以及简单的代码实现,当时我们的切点是用execution表达式来配置的,这种方式有一些局限性在里面: 灵活性不高,一个表达式只能切到某种同类型的方法 个性化不 ...

  5. spring中aop拦截自定义注解不生效

    前言 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206/six-finger 种一棵树最好的时间是十年前,其次是现在 我知道很多人不玩qq ...

  6. Spring AOP 切面@Around注解的具体使用

    @Around注解可以用来在调用一个具体方法前和调用后来完成一些具体的任务. 比如我们想在执行controller中方法前打印出请求参数,并在方法执行结束后来打印出响应值,这个时候,我们就可以借助于@ ...

  7. spring aop拦截自定义注解的切入点表达式

    @within(com.cxh.study.aop.controller.UserAccessAnnotation) 表示拦截含有com.cxh.study.aop.controller.UserAc ...

  8. 一个简单的例子,学习自定义注解和AOP

    转载自   一个简单的例子,学习自定义注解和AOP 记得今年年初刚开始面试的时候,被问的最多的就是你知道Spring的两大核心嘛?那你说说什么是AOP,什么是IOC?我相信你可能也被问了很多次了. 1 ...

  9. Spring AOP自定义注解并获取注解的参数

    环境 springboot:1.5 Intellij IDEA:2021.1 序言 最近有个需求,要做方法层面的权限控制.以前在公司使用的是spring security,然后使用注解 如下: @Pr ...

  10. 自定义注解妙用,一行代码搞定用户操作日志记录,你学会了吗?

    来源:https://blog.csdn.net/yjt520557/article/details/85099115 | 简介 我在使用spring完成项目的时候需要完成记录日志,我开始以为Spri ...

最新文章

  1. 全面认识一下.NET 4.0的缓存功能
  2. 引导分区 pbr 数据分析_如何在1小时内引导您的分析
  3. 睡醒了,有精神了,简单讲讲这几天的故事了
  4. 4.4 使用STM32控制MC20进行GPS帧数据解析
  5. Android 应用程序发布流程---碗豆荚发布流程
  6. DevNet网站上线
  7. 算法设计与分析——递归与分治策略——全排列
  8. Linux scp 指令
  9. n型半导体和p型半导体的区别_VNX系列大流量工业型膜堆, 为半导体等行业提供超纯水!...
  10. 石头剪刀布python代码_python实现石头剪刀布程序
  11. java css路径_java web开发中CSS路径有问题吗,运行jsp文件为什么找不到css文件?...
  12. GIS应用实例--模型预测、多元回归、空间自相关分析
  13. 简单粗暴日文键盘布局改为其他语言键盘布局
  14. 开关switch系列:android Switch显示文字
  15. 华为什么型号支持鸿蒙,哪些华为手机支持鸿蒙系统?华为支持鸿蒙OS手机型号清单...
  16. 高并发下的Nginx优化
  17. No pyvenv.cfg file
  18. C#循环体内定义对象/变量
  19. Linux Ubuntu系统设置成中文语言
  20. dsm是基于linux什么版本,DSM 文件扩展名: 它是什么以及如何打开它?

热门文章

  1. Python3 网络爬虫 <教程全集>
  2. 通过ARP查询目标路由器的MAC地址
  3. 实现Http Server
  4. 区块链开发入门到精通
  5. openstack-KVM-vCPU
  6. fiddler和wireshark对比
  7. 实战之8051驱动8位数码管
  8. kindle安装插件和koreader
  9. 微信小程序如何跳转视频号直播间
  10. 拉格朗日插值公式详解