From 5116aac80960da02d03b146412e3cf6229569437 Mon Sep 17 00:00:00 2001 From: Zerroi Date: Mon, 15 Apr 2024 20:20:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B3=A8=E8=A7=A3=E5=BC=8F=E6=97=A5?= =?UTF-8?q?=E5=BF=97):=20=E4=BD=BF=E7=94=A8SpringAOP=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E5=BC=8F=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 我们在企业级的开发中,必不可少的是对日志的记录, 实现有很多种方式,常见的就是基于AOP+注解进行保存, 但是考虑到程序的流畅和效率,我们可以使用异步进行保存, 小编最近在spring和springboot源码中看到有很多的监听处理贯穿前后:这就是著名的观察者模式!! --- .gitignore | 33 ++++++ pom.xml | 103 ++++++++++++++++++ .../AnnotationLogsApplication.java | 15 +++ .../annotationlogs/annotations/Log.java | 25 +++++ .../annotationlogs/aspect/SysLogAspect.java | 64 +++++++++++ .../config/EventPubListener.java | 18 +++ .../config/MyEventListener.java | 27 +++++ .../constant/BusinessTypeEnum.java | 41 +++++++ .../controller/UserController.java | 26 +++++ .../zerroi/annotationlogs/domain/SysLog.java | 66 +++++++++++ .../annotationlogs/mapper/SysMapper.java | 10 ++ .../annotationlogs/mapper/UserMapper.java | 7 ++ .../com/zerroi/annotationlogs/pojo/User.java | 11 ++ .../annotationlogs/service/SysService.java | 8 ++ .../annotationlogs/service/UserService.java | 7 ++ .../service/impl/SysServiceImpl.java | 21 ++++ .../service/impl/UserServiceImpl.java | 11 ++ .../zerroi/annotationlogs/utils/IpUtils.java | 68 ++++++++++++ src/main/resources/application.yml | 11 ++ src/main/resources/static/index.html | 6 + .../AnnotationLogsApplicationTests.java | 13 +++ 21 files changed, 591 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/com/zerroi/annotationlogs/AnnotationLogsApplication.java create mode 100644 src/main/java/com/zerroi/annotationlogs/annotations/Log.java create mode 100644 src/main/java/com/zerroi/annotationlogs/aspect/SysLogAspect.java create mode 100644 src/main/java/com/zerroi/annotationlogs/config/EventPubListener.java create mode 100644 src/main/java/com/zerroi/annotationlogs/config/MyEventListener.java create mode 100644 src/main/java/com/zerroi/annotationlogs/constant/BusinessTypeEnum.java create mode 100644 src/main/java/com/zerroi/annotationlogs/controller/UserController.java create mode 100644 src/main/java/com/zerroi/annotationlogs/domain/SysLog.java create mode 100644 src/main/java/com/zerroi/annotationlogs/mapper/SysMapper.java create mode 100644 src/main/java/com/zerroi/annotationlogs/mapper/UserMapper.java create mode 100644 src/main/java/com/zerroi/annotationlogs/pojo/User.java create mode 100644 src/main/java/com/zerroi/annotationlogs/service/SysService.java create mode 100644 src/main/java/com/zerroi/annotationlogs/service/UserService.java create mode 100644 src/main/java/com/zerroi/annotationlogs/service/impl/SysServiceImpl.java create mode 100644 src/main/java/com/zerroi/annotationlogs/service/impl/UserServiceImpl.java create mode 100644 src/main/java/com/zerroi/annotationlogs/utils/IpUtils.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/static/index.html create mode 100644 src/test/java/com/zerroi/annotationlogs/AnnotationLogsApplicationTests.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..24c498b --- /dev/null +++ b/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + com.zerroi + AnnotationLogs + 0.0.1-SNAPSHOT + AnnotationLogs + AnnotationLogs + + 17 + UTF-8 + UTF-8 + 3.0.2 + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.6 + + + + com.alibaba + druid-spring-boot-starter + 1.1.16 + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-web + + + + com.mysql + mysql-connector-j + runtime + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 17 + 17 + UTF-8 + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + com.zerroi.annotationlogs.AnnotationLogsApplication + true + + + + repackage + + repackage + + + + + + + + diff --git a/src/main/java/com/zerroi/annotationlogs/AnnotationLogsApplication.java b/src/main/java/com/zerroi/annotationlogs/AnnotationLogsApplication.java new file mode 100644 index 0000000..947a16c --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/AnnotationLogsApplication.java @@ -0,0 +1,15 @@ +package com.zerroi.annotationlogs; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.zerroi.annotationlogs.mapper") +public class AnnotationLogsApplication { + + public static void main(String[] args) { + SpringApplication.run(AnnotationLogsApplication.class, args); + } + +} diff --git a/src/main/java/com/zerroi/annotationlogs/annotations/Log.java b/src/main/java/com/zerroi/annotationlogs/annotations/Log.java new file mode 100644 index 0000000..b8c24c5 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/annotations/Log.java @@ -0,0 +1,25 @@ +package com.zerroi.annotationlogs.annotations; + +import com.zerroi.annotationlogs.constant.BusinessTypeEnum; +import java.lang.annotation.*; + +/** + * 自定义操作日志记录注解 + * @author zerroi + */ +@Target(ElementType.METHOD) // 注解只能用于方法 +@Retention(RetentionPolicy.RUNTIME) // 修饰注解的生命周期 +@Documented +public @interface Log { + + String value() default ""; + /** + * 模块 + */ + String title() default "测试模块"; + + /** + * 功能 + */ + BusinessTypeEnum businessType() default BusinessTypeEnum.OTHER; +} diff --git a/src/main/java/com/zerroi/annotationlogs/aspect/SysLogAspect.java b/src/main/java/com/zerroi/annotationlogs/aspect/SysLogAspect.java new file mode 100644 index 0000000..b7fdbc7 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/aspect/SysLogAspect.java @@ -0,0 +1,64 @@ +package com.zerroi.annotationlogs.aspect; + +import com.zerroi.annotationlogs.annotations.Log; +import com.zerroi.annotationlogs.config.EventPubListener; +import com.zerroi.annotationlogs.domain.SysLog; +import com.zerroi.annotationlogs.utils.IpUtils; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.time.LocalDateTime; + +@Aspect +@Component +public class SysLogAspect { + + private final Logger logger = LoggerFactory.getLogger(SysLogAspect.class); + + @Resource + private EventPubListener eventPubListener; + + /** + * 以注解所标注的方法作为切入点 + */ + @Pointcut("@annotation(com.zerroi.annotationlogs.annotations.Log)") + public void sysLog() {} + + + /** + * 在切点之后织入 + */ + @After("sysLog()") + public void doAfter(JoinPoint joinPoint) { + Log log = ((MethodSignature) joinPoint.getSignature()).getMethod() + .getAnnotation(Log.class); + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder + .getRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + String method = request.getMethod(); + String url = request.getRequestURL().toString(); + String ip = IpUtils.getIpAddr(request); + SysLog sysLog = new SysLog(); + sysLog.setBusinessType(log.businessType().getCode()); + sysLog.setTitle(log.title()); + sysLog.setRequestMethod(method); + sysLog.setOperIp(ip); + sysLog.setOperUrl(url); + // 从登录中token获取登录人员信息即可 + sysLog.setOperName("我是测试人员"); + sysLog.setOperTime(LocalDateTime.now()); + // 发布消息 + eventPubListener.pushListener(sysLog); + logger.info("=======日志发送成功,内容:{}",sysLog); + } +} diff --git a/src/main/java/com/zerroi/annotationlogs/config/EventPubListener.java b/src/main/java/com/zerroi/annotationlogs/config/EventPubListener.java new file mode 100644 index 0000000..b3b44a9 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/config/EventPubListener.java @@ -0,0 +1,18 @@ +package com.zerroi.annotationlogs.config; + +import com.zerroi.annotationlogs.domain.SysLog; +import jakarta.annotation.Resource; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +@Component +public class EventPubListener { + @Resource + private ApplicationContext applicationContext; + + // 事件发布方法 + public void pushListener(SysLog sysLogEvent) { + applicationContext.publishEvent(sysLogEvent); + } +} + diff --git a/src/main/java/com/zerroi/annotationlogs/config/MyEventListener.java b/src/main/java/com/zerroi/annotationlogs/config/MyEventListener.java new file mode 100644 index 0000000..bce3483 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/config/MyEventListener.java @@ -0,0 +1,27 @@ +package com.zerroi.annotationlogs.config; + +import com.zerroi.annotationlogs.domain.SysLog; +import com.zerroi.annotationlogs.service.SysService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class MyEventListener { + + @Resource + private SysService sysService; + + // 开启异步 + @Async + // 开启监听 + @EventListener(SysLog.class) + public void saveSysLog(SysLog event) { + log.info("=====即将异步保存到数据库======"); + sysService.saveLog(event); + } + +} diff --git a/src/main/java/com/zerroi/annotationlogs/constant/BusinessTypeEnum.java b/src/main/java/com/zerroi/annotationlogs/constant/BusinessTypeEnum.java new file mode 100644 index 0000000..c36e949 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/constant/BusinessTypeEnum.java @@ -0,0 +1,41 @@ +package com.zerroi.annotationlogs.constant; + +public enum BusinessTypeEnum { + + /** + * 其它 + */ + OTHER(0,"其它"), + + /** + * 新增 + */ + INSERT(1,"新增"), + + /** + * 修改 + */ + UPDATE(2,"修改"), + + /** + * 删除 + */ + DELETE(3,"删除"); + + private Integer code; + + private String message; + + BusinessTypeEnum(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Integer getCode() { + return code; + } + + public String getMessage() { + return message; + } +} \ No newline at end of file diff --git a/src/main/java/com/zerroi/annotationlogs/controller/UserController.java b/src/main/java/com/zerroi/annotationlogs/controller/UserController.java new file mode 100644 index 0000000..c1eb137 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/controller/UserController.java @@ -0,0 +1,26 @@ +package com.zerroi.annotationlogs.controller; + +import com.zerroi.annotationlogs.annotations.Log; +import com.zerroi.annotationlogs.constant.BusinessTypeEnum; +import com.zerroi.annotationlogs.pojo.User; +import com.zerroi.annotationlogs.service.UserService; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/user") +public class UserController { + + @Resource + private UserService userService; + + @GetMapping("/getById/{id}") + @Log(title = "根据id获取用户信息", businessType = BusinessTypeEnum.OTHER) + public User getUser(@PathVariable("id") Integer id) { + return userService.getById(id); + } + +} diff --git a/src/main/java/com/zerroi/annotationlogs/domain/SysLog.java b/src/main/java/com/zerroi/annotationlogs/domain/SysLog.java new file mode 100644 index 0000000..28da32e --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/domain/SysLog.java @@ -0,0 +1,66 @@ +package com.zerroi.annotationlogs.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 操作日志记录表 sys_log + * + */ +@Data +@TableName("sys_log") +public class SysLog implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 日志主键 + */ + @TableId + private Long id; + + /** + * 操作模块 + */ + private String title; + + /** + * 业务类型(0其它 1新增 2修改 3删除) + */ + private Integer businessType; + + /** + * 请求方式 + */ + private String requestMethod; + + /** + * 操作人员 + */ + private String operName; + + /** + * 请求url + */ + private String operUrl; + + /** + * 操作地址 + */ + private String operIp; + + /** + * 操作时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime operTime; + +} + + diff --git a/src/main/java/com/zerroi/annotationlogs/mapper/SysMapper.java b/src/main/java/com/zerroi/annotationlogs/mapper/SysMapper.java new file mode 100644 index 0000000..12b04f6 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/mapper/SysMapper.java @@ -0,0 +1,10 @@ +package com.zerroi.annotationlogs.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zerroi.annotationlogs.domain.SysLog; +import org.apache.ibatis.annotations.Mapper; +import org.mybatis.spring.annotation.MapperScan; + +@Mapper +public interface SysMapper extends BaseMapper { +} diff --git a/src/main/java/com/zerroi/annotationlogs/mapper/UserMapper.java b/src/main/java/com/zerroi/annotationlogs/mapper/UserMapper.java new file mode 100644 index 0000000..275d471 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/mapper/UserMapper.java @@ -0,0 +1,7 @@ +package com.zerroi.annotationlogs.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zerroi.annotationlogs.pojo.User; + +public interface UserMapper extends BaseMapper { +} diff --git a/src/main/java/com/zerroi/annotationlogs/pojo/User.java b/src/main/java/com/zerroi/annotationlogs/pojo/User.java new file mode 100644 index 0000000..a0773c0 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/pojo/User.java @@ -0,0 +1,11 @@ +package com.zerroi.annotationlogs.pojo; + +import lombok.Data; + +@Data +public class User { + private Integer id; + private String name; + private Integer age; + private String email; +} diff --git a/src/main/java/com/zerroi/annotationlogs/service/SysService.java b/src/main/java/com/zerroi/annotationlogs/service/SysService.java new file mode 100644 index 0000000..b201ba4 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/service/SysService.java @@ -0,0 +1,8 @@ +package com.zerroi.annotationlogs.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.zerroi.annotationlogs.domain.SysLog; + +public interface SysService { + int saveLog(SysLog sysLog); +} diff --git a/src/main/java/com/zerroi/annotationlogs/service/UserService.java b/src/main/java/com/zerroi/annotationlogs/service/UserService.java new file mode 100644 index 0000000..d69493a --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/service/UserService.java @@ -0,0 +1,7 @@ +package com.zerroi.annotationlogs.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.zerroi.annotationlogs.pojo.User; + +public interface UserService extends IService { +} diff --git a/src/main/java/com/zerroi/annotationlogs/service/impl/SysServiceImpl.java b/src/main/java/com/zerroi/annotationlogs/service/impl/SysServiceImpl.java new file mode 100644 index 0000000..8ae9803 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/service/impl/SysServiceImpl.java @@ -0,0 +1,21 @@ +package com.zerroi.annotationlogs.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerroi.annotationlogs.domain.SysLog; +import com.zerroi.annotationlogs.mapper.SysMapper; +import com.zerroi.annotationlogs.service.SysService; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class SysServiceImpl implements SysService { + + @Autowired + private SysMapper sysMapper; + + @Override + public int saveLog(SysLog sysLog) { + return sysMapper.insert(sysLog); + } +} diff --git a/src/main/java/com/zerroi/annotationlogs/service/impl/UserServiceImpl.java b/src/main/java/com/zerroi/annotationlogs/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..0b30648 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/service/impl/UserServiceImpl.java @@ -0,0 +1,11 @@ +package com.zerroi.annotationlogs.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.zerroi.annotationlogs.mapper.UserMapper; +import com.zerroi.annotationlogs.pojo.User; +import com.zerroi.annotationlogs.service.UserService; +import org.springframework.stereotype.Service; + +@Service +public class UserServiceImpl extends ServiceImpl implements UserService { +} diff --git a/src/main/java/com/zerroi/annotationlogs/utils/IpUtils.java b/src/main/java/com/zerroi/annotationlogs/utils/IpUtils.java new file mode 100644 index 0000000..b106f59 --- /dev/null +++ b/src/main/java/com/zerroi/annotationlogs/utils/IpUtils.java @@ -0,0 +1,68 @@ +package com.zerroi.annotationlogs.utils; + +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import jakarta.servlet.http.HttpServletRequest; + +public class IpUtils { + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) { + if (request == null) { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) { + if (!isUnknown(subIp)) { + ip = subIp; + break; + } + } + } + return ip; + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..354e6cf --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,11 @@ +server: + port: 8088 + +spring: + datasource: + #使用阿里的Druid + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.249.131:3306/test?serverTimezone=UTC + username: root + password: root \ No newline at end of file diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html new file mode 100644 index 0000000..89bb8ba --- /dev/null +++ b/src/main/resources/static/index.html @@ -0,0 +1,6 @@ + + +

hello word!!!

+

this is a html page

+ + \ No newline at end of file diff --git a/src/test/java/com/zerroi/annotationlogs/AnnotationLogsApplicationTests.java b/src/test/java/com/zerroi/annotationlogs/AnnotationLogsApplicationTests.java new file mode 100644 index 0000000..69d02c0 --- /dev/null +++ b/src/test/java/com/zerroi/annotationlogs/AnnotationLogsApplicationTests.java @@ -0,0 +1,13 @@ +package com.zerroi.annotationlogs; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AnnotationLogsApplicationTests { + + @Test + void contextLoads() { + } + +}