一、日志管理系统
1.1目标效果图
1.2创建日志表,和对应的实体类
@Data
public class SysLog implements Serializable {
private static final long serialVersionUID = 1123526L;
private Integer id;
private String module;
private String content;
private Integer createId;
private Timestamp createTime;
private String uname;
}
1.3controller
@RestController
@RequestMapping("syslog")
public class SysLogController {
@Autowired
private SysLogService sysLogService;
@GetMapping("findAll")
public Result findAll() {
return Result.success(sysLogService.findAll());
}
}
1.4service
public interface SysLogService {
List<SysLog> findAll();
}
@Service
public class SysLogServiceImpl implements SysLogService {
@Autowired
private SysLogMapper sysLogMapper;
@Override
public List<SysLog> findAll() {
return sysLogMapper.findAll();
}
}
1.5mapper
public interface SysLogMapper extends BaseMapper<SysLog> {
List<SysLog> findAll();
@Override
@Insert("insert into sys_log values (null, #{module}, #{content}, #{createId}, now())")
boolean insert(SysLog sysLog);
}
注意:因为要返回给前端创建人的姓名,所以连表查询了,将name一块返回了
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itqq.mapper.SysLogMapper">
<select id="findAll" resultType="SysLog">
SELECT s.*, u.username AS uname FROM sys_log s
left JOIN `user` u ON s.create_id = u.id
</select>
</mapper>
1.6核心代码配置Aop切面,监控所有带注解的接口方法
1.6.1注解类LogAnnotation的配置
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
String module() default "";
String content() default "";
}
通过注解获取操作模块和操作内容
比如:student模块
@LogAnnotation(module = "学生模块",content = "查询所有学生")
@GetMapping("findAll")
public Result findAll(){
return Result.success(studentService.findAll());
}
@GetMapping("fidById/{id}")
@LogAnnotation(module = "学生模块",content = "查询id学生")
public Result fidById(@PathVariable int id){
return Result.success(studentService.findById(id));
}
@PostMapping("add")
@LogAnnotation(module = "学生模块",content = "添加学生")
public Result add(@RequestBody Student student){
studentService.add(student);
return Result.success(student);
}
1.6.2重要**通过Spring Aop和自定义注解@LogAnnotation,来监控和记录方法调用
package com.itqq.config;
import com.itqq.mapper.SysLogMapper;
import com.itqq.pojo.SysLog;
import com.itqq.utils.LogAnnotation;
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.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
* Created with IntelliJ IDEA.
*
* @Author: syq
* @Date: 2024/07/04
* @Description:
*/
@Component
@Aspect
@Slf4j
public class SysLogAop {
@Autowired
private SysLogMapper sysLogMapper;
@Autowired
private HttpServletRequest request;
// 切面com.itqq.utils.LogAnnotation根据这个类进行切
@Around("@annotation(com.itqq.utils.LogAnnotation)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 将程序执行的某个点(调用方法等) 返回的值保存在result中
Object result = joinPoint.proceed();
// 获取joinPoint的签名--即描述了方法的信息,如方法名、参数类型等
// 由于签名可能有多种类型,但在这里我们关心的是方法签名,所以将其转换为MethodSignature类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 从MethodSignature对象中获取实际的Method对象,这将提供对方法的完整反射访问,包括其注解和参数。
Method method = signature.getMethod();
// 利用反射机制从Method对象中获取@LogAnnotation注解的实例
// 如果方法上没有这个注解,则返回null。
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
log.info("{}",logAnnotation);
// 创建一个SysLog对象实例,用于封装系统日志的相关信息。
SysLog sysLog = new SysLog();
// 从@LogAnnotation注解中提取module和content属性的值,并分别设置为SysLog对象的module和content字段。
sysLog.setModule(logAnnotation.module());
sysLog.setContent(logAnnotation.content());
// 获取用户id---从TokenInterceptor拦截器传来的userId
Integer userId = (Integer) request.getAttribute("userId");
sysLog.setCreateId(userId);
sysLogMapper.insert(sysLog);
return result;
}
}
通过这种方式,每当带有@LogAnnotation的方法被调用时,Spring AOP都会自动执行SysLogAop
中的around()
方法,从而记录下方法的操作模块和操作内容,实现对方法操作的间接监控和日志记录。这种方法不仅减少了代码侵入性,也使得日志功能更加模块化和易于管理。
1.7展望
除了AOP之外,日志系统还可以通过过滤器(Filters)和拦截器(Interceptors)来实现,这两种机制在Web应用中尤其常见,它们各自有独特的应用场景和优势:
过滤器(Filters)
过滤器是Servlet规范的一部分,它们在请求到达目标资源(如Servlet、JSP页面或其他过滤器)之前执行,也可以在响应返回客户端之前执行。因此,过滤器非常适合用于全局性的请求处理,如日志记录、安全性检查、编码转换等。
如何使用过滤器实现日志记录:
-
创建过滤器类:实现
javax.servlet.Filter
接口或继承javax.servlet.GenericFilter
抽象类,覆盖init()
,doFilter()
, 和destroy()
方法。在doFilter()
方法中,可以记录请求的细节,如请求URI、请求方法、请求时间等。 -
配置过滤器:在
web.xml
配置文件中,声明过滤器并通过<filter-mapping>
元素将其映射到需要拦截的URL模式上,或者在Java配置类中使用DelegatingFilterProxy
进行配置。
拦截器(Interceptors)
拦截器是Spring MVC框架提供的特性,主要用于处理MVC架构中的请求。它们在控制器方法执行之前和之后执行,可以用于执行预处理(如认证、授权)、后处理(如关闭资源、记录日志)等任务。
如何使用拦截器实现日志记录:
-
创建拦截器类:实现
org.springframework.web.servlet.handler.HandlerInterceptor
接口,覆盖preHandle()
,postHandle()
, 和afterCompletion()
方法。在preHandle()
方法中可以记录请求开始的信息,在afterCompletion()
方法中记录请求完成的信息。 -
配置拦截器:在Spring MVC的配置类中,通过实现
WebMvcConfigurer
接口的addInterceptors()
方法,注册拦截器并配置其拦截的URL模式。
比较与选择
-
全局性:过滤器适用于所有Servlet请求,无论请求的目标是哪个具体的Servlet或JSP页面,而拦截器只作用于Spring MVC的控制器请求。
-
灵活性:拦截器提供了更多的控制点(preHandle, postHandle, afterCompletion),可以更细致地控制请求的处理流程,而过滤器主要在请求的开始和结束时执行。
-
集成性:拦截器紧密集成在Spring MVC框架内,可以方便地访问Spring IoC容器中的bean,而过滤器相对独立,获取Spring bean需要额外的配置。
选择过滤器还是拦截器实现日志系统,主要取决于具体的应用场景和需求。如果你的应用基于Spring MVC,且日志记录需要与框架的其他特性紧密结合,那么拦截器可能是更好的选择。反之,如果你希望实现一个更通用的日志系统,不依赖于特定的框架,或者需要处理非Spring MVC的请求,过滤器则更为合适。