尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【JAVA进阶篇教学】第十六篇:Java中AOP使用

【JAVA进阶篇教学】第十六篇:Java中AOP使用 博主打算从0-1讲解下java进阶篇教学今天教学第十五篇Java中AOP使用。 AOPAspect-Oriented Programming是一种编程范式它允许开发者在不修改源代码的情况下对代码进行横切关注点的分离和增强。在 Java 中AOP 通常通过使用 Spring Framework 或 AspectJ 等框架来实现。 在这篇博客中我们将介绍 AOP 的基本概念、使用场景以及如何在 Java 中使用 Spring Framework 实现 AOP。目录一、前言二、AOP 的基本概念三、AOP 的使用场景四、实现 AOP4.1新建注解Log4.2新建LogAspect类4.1.1依赖4.1.2表结构4.1.3实体类4.1.4说明4.1.5怎么调用??4.1.6版本号五、总结* *一、前言----AOPAspect-Oriented Programming是一种编程范式它允许开发者在不修改源代码的情况下对代码进行横切关注点的分离和增强。在 Java 中AOP 通常通过使用 Spring Framework 或 AspectJ 等框架来实现。二、AOP 的基本概念-----------AOP 是一种面向切面编程的思想它将程序中的业务逻辑和系统级服务如日志记录、事务管理、权限控制等分离开来使得系统级服务可以在不修改业务逻辑代码的情况下进行添加、修改或删除。AOP 中的核心概念包括切面Aspect切面是一个关注点的集合它定义了在哪些地方需要进行增强。*连接点JoinPoint连接点是程序执行过程中的一个点如方法调用、异常抛出等。*通知Advice通知是在连接点处执行的操作它可以是前置通知、后置通知、异常通知等。*切点Pointcut切点是定义在连接点上的一个表达式用于匹配哪些连接点需要进行增强。通过使用切面、连接点、通知和切点等概念AOP 可以实现对程序的横切关注点的分离和增强提高代码的可读性、可维护性和可扩展性。三、AOP 的使用场景-----------AOP 可以用于以下场景*日志记录可以在方法调用前后记录日志信息方便进行调试和问题排查。*事务管理可以在方法调用前后进行事务的开启、提交和回滚保证数据的一致性。*权限控制可以在方法调用前进行权限检查确保只有授权的用户才能访问特定的资源。*性能监控可以在方法调用前后记录性能指标方便进行性能优化。*异常处理可以在方法调用后进行异常处理避免程序崩溃。四、实现 AOP--------Spring Framework 是一个轻量级的 Java 开发框架它提供了对 AOP 的支持。在 Spring Framework 中可以使用AspectJ 注解来定义切面使用Pointcut 注解来定义切点使用Before、After、AfterReturning 和AfterThrowing 等注解来定义通知。那么今天就以日志记录来举例。### 4.1新建注解Log import java.lang.annotation.; Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface Log { /** * 操作模块 * return/ String modul() default “”; /* * 操作类型 * return/ String type() default “”; /* * 操作说明 * return/ String desc() default “”; } ### 4.2新建LogAspect类 import com.alibaba.fastjson.JSON; import com.hvit.user.util.MyUtils; import com.hvit.user.yst.config.annotation.Log; import com.hvit.user.yst.entity.LogErrorInfoEntity; import com.hvit.user.yst.entity.LogInfoEntity; import com.hvit.user.yst.service.LogErrorInfoService; import com.hvit.user.yst.service.LogInfoService; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.; /*** * 切面处理类操作日志异常日志记录处理/ Aspect Component public class LogAspect { /**项目发布版本号/ Value(“${version}”) private String version; /* 统计请求的处理时间/ ThreadLocal startTime new ThreadLocal(); Autowired private LogInfoService logInfoService; Autowired private LogErrorInfoService logErrorInfoService; /* * methodNamelogPoinCut * description设置操作日志切入点 记录操作日志 在注解的位置切入代码 * authorcaozhen * dateTime2024-05-15 * Params [] * Return void * editNote/ Pointcut(“annotation(com.hvit.user.yst.config.annotation.Log)”) public void logPoinCut() { } /* * methodNameexceptionLogPoinCut * description设置操作异常切入点记录异常日志 扫描所有controller包下操作 * authorcaozhen * dateTime2024-05-15 * Params [] * Return void * editNote/ Pointcut(execution(com.hvit.user.yst.controller….(…))) public void exceptionLogPoinCut() { } Before(“logPoinCut()”) public void doBefore() { // 接收到请求记录请求开始时间 startTime.set(System.currentTimeMillis()); } /** * methodNamedoAfterReturning * description正常返回通知拦截用户操作日志连接点正常执行完成后执行 如果连接点抛出异常则不会执行 * authorcaozhen * dateTime2024-05-15 * Params [joinPoint, keys] * Return void * editNote/ AfterReturning(value “logPoinCut()”, returning “keys”) public void doAfterReturning(JoinPoint joinPoint, Object keys) { // 获取RequestAttributes RequestAttributes requestAttributes RequestContextHolder.getRequestAttributes(); // 从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequest request (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); LogInfoEntity logInfo LogInfoEntity.builder().build(); try { // 从切面织入点处通过反射机制获取织入点处的方法 MethodSignature signature (MethodSignature) joinPoint.getSignature(); // 获取切入点所在的方法 Method method signature.getMethod(); // 获取请求的类名 String className joinPoint.getTarget().getClass().getName(); // 获取操作 Log log method.getAnnotation(Log.class); if (Objects.nonNull(log)) { logInfo.setModule(log.modul()); logInfo.setType(log.type()); logInfo.setMessage(log.desc()); } logInfo.setMethod(className “.” method.getName()); // 请求的方法名 logInfo.setReqParam(JSON.toJSONString(converMap(request.getParameterMap()))); // 请求参数 //logInfo.setResParam(JSON.toJSONString(keys)); // 返回结果 logInfo.setUserId(request.getAttribute(“uuid”) null ? “未知” : request.getAttribute(“uuid”).toString()); // 请求用户ID logInfo.setUserName(request.getAttribute(“userName”) null ? “未知” : request.getAttribute(“userName”).toString()); // 请求用户名称 logInfo.setIp(MyUtils.getIpAddr()); // 请求IP logInfo.setUri(request.getRequestURI()); // 请求URI logInfo.setCreateTime(new Date()); // 创建时间 logInfo.setVersion(version); // 操作版本 logInfo.setTakeUpTime(System.currentTimeMillis() - startTime.get()); // 耗时 logInfoService.save(logInfo); } catch (Exception e) { e.printStackTrace(); } } /* * methodNamedoAfterThrowing * description异常返回通知用于拦截异常日志信息 连接点抛出异常后执行 * authorcaozhen * dateTime2024-05-15 * Params [joinPoint, e] * Return void * editNote/ AfterThrowing(pointcut “exceptionLogPoinCut()”, throwing “e”) public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { // 获取RequestAttributes RequestAttributes requestAttributes RequestContextHolder.getRequestAttributes(); // 从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequest request (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); try { // 从切面织入点处通过反射机制获取织入点处的方法 MethodSignature signature (MethodSignature) joinPoint.getSignature(); // 获取切入点所在的方法 Method method signature.getMethod(); // 获取请求的类名 String className joinPoint.getTarget().getClass().getName(); logErrorInfoService.save( LogErrorInfoEntity.builder() .reqParam(JSON.toJSONString(converMap(request.getParameterMap()))) // 请求参数 .method(className “.” method.getName()) // 请求方法名 .name(e.getClass().getName()) // 异常名称 .message(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace())) // 异常信息 .userId(request.getAttribute(“uuid”) null ? “未知” : request.getAttribute(“uuid”).toString()) // 操作员ID .userName(request.getAttribute(“userName”) null ? “未知” : request.getAttribute(“userName”).toString()) // 操作员名称 .uri(request.getRequestURI()) // 操作URI .ip(MyUtils.getIpAddr()) // 操作员IP .version(version) // 版本号 .createTime(new Date()) // 发生异常时间 .build() ); } catch (Exception e2) { e2.printStackTrace(); } } /* * methodNameconverMap * description转换request 请求参数 * authorcaozhen * dateTime2024-05-15 * Params [paramMap] * Return java.util.Mapjava.lang.String, java.lang.String * editNote/ public MapString, String converMap(MapString, String[] paramMap) { MapString, String rtnMap new HashMapString, String(); for (String key : paramMap.keySet()) { rtnMap.put(key, paramMap.get(key)[0]); } return rtnMap; } /* * methodNamestackTraceToString * description转换异常信息为字符串 * authorcaozhen * dateTime2024-05-15 * Params [exceptionName, exceptionMessage, elements] * Return java.lang.String * editNote/ public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) { StringBuffer strbuff new StringBuffer(); for (StackTraceElement stet : elements) { strbuff.append(stet “”); } String message exceptionName “:” exceptionMessage “” strbuff.toString(); return message; } }#### 4.1.1依赖在LogAspect我们有用fastjson这个依赖包 com.alibaba fastjson 1.2.83 #### 4.1.2表结构 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0; – ---------------------------- – Table structure for t_log_error_info – ---------------------------- DROP TABLE IF EXISTSt_log_error_info; CREATE TABLEt_log_error_info(uuidvarchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,req_paramvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘请求参数’,namevarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘异常名称’,messagelongtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT ‘异常信息’,user_idvarchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘操作用户id’,user_namevarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘操作用户名称’,methodvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘请求方法’,urivarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘请求url’,ipvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘请求IP’,versionvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘版本号’,create_timedatetime NULL DEFAULT NULL COMMENT ‘创建时间’, PRIMARY KEY (uuid) USING BTREE ) ENGINE InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT ‘操作日志异常信息’ ROW_FORMAT DYNAMIC; – ---------------------------- – Table structure for t_log_info – ---------------------------- DROP TABLE IF EXISTSt_log_info; CREATE TABLEt_log_info(uuidvarchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,modulevarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘功能模块’,typevarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘操作类型’,messagevarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘操作描述’,req_paramtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT ‘请求参数’,res_paramlongtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT ‘响应参数’,take_up_timeint(10) NULL DEFAULT NULL COMMENT ‘耗时’,user_idvarchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘操作用户id’,user_namevarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘操作用户名称’,methodvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘操作方面’,urivarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘请求url’,ipvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘请求IP’,versionvarchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT ‘版本号’,create_timedatetime NULL DEFAULT NULL COMMENT ‘创建时间’, PRIMARY KEY (uuid) USING BTREE ) ENGINE InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci COMMENT ‘操作日志表’ ROW_FORMAT DYNAMIC; SET FOREIGN_KEY_CHECKS 1; #### 4.1.3实体类LogInfoEntity import com.baomidou.mybatisplus.annotations.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import com.hvit.user.yst.entity.base.IdEntity; import io.swagger.annotations.ApiModelProperty; import lombok.; import lombok.experimental.Accessors; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.util.Date; /** ** 操作日志表 ** * author 曹震 * since 2024-05-15/ TableName(“t_log_info”) Data JsonInclude Builder public class LogInfoEntity extends IdEntity implements Serializable { public LogInfoEntity(String module, String type, String message, String reqParam, String resParam, Long takeUpTime, String userId, String userName, String method, String uri, String ip, String version, Date createTime) { this.module module; this.type type; this.message message; this.reqParam reqParam; this.resParam resParam; this.takeUpTime takeUpTime; this.userId userId; this.userName userName; this.method method; this.uri uri; this.ip ip; this.version version; this.createTime createTime; } public LogInfoEntity(){ } private static final long serialVersionUID1L; /* * 功能模块/ ApiModelProperty(name “module”, value “功能模块”) private String module; /* * 操作类型/ ApiModelProperty(name “type”, value “操作类型”) private String type; /* * 操作描述/ ApiModelProperty(name “message”, value “操作描述”) private String message; /* * 请求参数/ ApiModelProperty(name “reqParam”, value “请求参数”) private String reqParam; /* * 响应参数/ ApiModelProperty(name “resParam”, value “响应参数”) private String resParam; /* * 耗时/ ApiModelProperty(name “takeUpTime”, value “耗时”) private Long takeUpTime; /* * 操作用户id/ ApiModelProperty(name “userId”, value “操作用户id”) private String userId; /* * 操作用户名称/ ApiModelProperty(name “userName”, value “操作用户名称”) private String userName; /* * 操作方面/ ApiModelProperty(name “method”, value “操作方面”) private String method; /* * 请求url/ ApiModelProperty(name “uri”, value “请求url”) private String uri; /* * 请求IP/ ApiModelProperty(name “ip”, value “请求IP”) private String ip; /* * 版本号/ ApiModelProperty(name “version”, value “版本号”) private String version; /* * 创建时间/ ApiModelProperty(name “createTime”, value “创建时间”) DateTimeFormat(pattern “yyyy-MM-dd HH:mm:ss”, iso DateTimeFormat.ISO.DATE) JsonFormat(pattern “yyyy-MM-dd HH:mm:ss”, timezone “GMT8”) private Date createTime; } LogErrorInfoEntity import com.baomidou.mybatisplus.annotations.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import com.hvit.user.yst.entity.base.IdEntity; import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.util.Date; /* ** 操作日志异常信息 ** * author 曹震 * since 2024-05-15/ TableName(“t_log_error_info”) Data JsonInclude Builder public class LogErrorInfoEntity extends IdEntity implements Serializable { public LogErrorInfoEntity(String reqParam, String name, String message, String userId, String userName, String method, String uri, String ip, String version, Date createTime) { this.reqParam reqParam; this.name name; this.message message; this.userId userId; this.userName userName; this.method method; this.uri uri; this.ip ip; this.version version; this.createTime createTime; } public LogErrorInfoEntity(){ } private static final long serialVersionUID1L; /* * 请求参数/ ApiModelProperty(name “reqParam”, value “请求参数”) private String reqParam; /* * 异常名称/ ApiModelProperty(name “name”, value “异常名称”) private String name; /* * 异常信息/ ApiModelProperty(name “message”, value “异常信息”) private String message; /* * 操作用户id/ ApiModelProperty(name “userId”, value “操作用户id”) private String userId; /* * 操作用户名称/ ApiModelProperty(name “userName”, value “操作用户名称”) private String userName; /* * 请求方法/ ApiModelProperty(name “method”, value “请求方法”) private String method; /* * 请求url/ ApiModelProperty(name “uri”, value “请求url”) private String uri; /* * 请求IP/ ApiModelProperty(name “ip”, value “请求IP”) private String ip; /* * 版本号/ ApiModelProperty(name “version”, value “版本号”) private String version; /* * 创建时间/ ApiModelProperty(name “createTime”, value “创建时间”) DateTimeFormat(pattern “yyyy-MM-dd HH:mm:ss”, iso DateTimeFormat.ISO.DATE) JsonFormat(pattern “yyyy-MM-dd HH:mm:ss”, timezone “GMT8”) private Date createTime; } IdEntity import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.enums.IdType; import java.io.Serializable; public class IdEntity implements Serializable { TableId(type IdType.INPUT) private String uuid; public String getUuid() { return uuid; } public void setUuid(String uuid) { this.uuid uuid; } } #### 4.1.4说明 Autowired private LogInfoService logInfoService; Autowired private LogErrorInfoService logErrorInfoService;Service类自行建立其实也就是mybatis-plus生成的代码。你们可以自行生成#### 4.1.5怎么调用 import com.hvit.user.util.Constants; import com.hvit.user.util.R; import com.hvit.user.yst.config.annotation.Log; import com.hvit.user.yst.service.AppServicesService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** * 测试/ RestController RequestMapping(“/xxxx/api/”) public class ApiController { Autowired private AppServicesService appServicesService; /* * 获取二维码门牌地址 */ Log(modul “日志-数字门牌对外接口”, type Constants.SELECT, desc “数字门牌对外接口”) RequestMapping(value “/v1/getQrcodeAddressInfo”, method RequestMethod.GET) ApiOperation(value “数字门牌对外接口”) public ResponseEntity? getQrcodeAddressInfo(String code, String appKey, String appSecret) { return ResponseEntity.ok(appServicesService.getQrcodeAddressInfo(code, appKey, appSecret)); } } 这样只要调用了这个接口那么正常调用日志或者异常日志都会保存进数据库中。#### 4.1.6版本号代码中有获取版本号的直接在yml文件新增如下配置 version: 2.0五、总结----AOP 是一种强大的编程范式它可以帮助开发者将系统级服务从业务逻辑中分离出来提高代码的可读性、可维护性和可扩展性。在 Java 中可以使用 Spring Framework 或 AspectJ 等框架来实现 AOP。通过使用 AOP可以实现日志记录、事务管理、权限控制、性能监控和异常处理等功能使代码更加健壮和易于维护。点个关注不会迷路
返回列表