一、 前言
本篇文章主要介绍eladmin中如何使用自定义注解实现日志记录,日志管理CRUD逻辑感兴趣的同学可以看源码学习------eladmin源码
二、自定义注解实现日志记录
2.1 定义注解
java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}
2.2 AOP三板斧
java
// 第一步:创建切面类
@Component
@Aspect
@Slf4j
public class LogAspect {
private final SysLogService sysLogService;
ThreadLocal<Long> currentTime = new ThreadLocal<>();
public LogAspect(SysLogService sysLogService) {
this.sysLogService = sysLogService;
}
/**
* 第二步:定义切入点
*/
@Pointcut("@annotation(me.zhengjie.annotation.Log)")
public void logPointcut() {
// 该方法无方法体,主要为了让同类中其他方法使用此切入点
}
/**
* 第三步:通知与切点整合
*
* @param joinPoint join point for advice
*/
@Around("logPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object result;
currentTime.set(System.currentTimeMillis());
result = joinPoint.proceed();
SysLog sysLog = new SysLog("INFO",System.currentTimeMillis() - currentTime.get());
currentTime.remove();
HttpServletRequest request = RequestHolder.getHttpServletRequest();
sysLogService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request),joinPoint, sysLog);
return result;
}
/**
* 配置异常通知
*
* @param joinPoint join point for advice
* @param e exception
*/
@AfterThrowing(pointcut = "logPointcut()", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
SysLog sysLog = new SysLog("ERROR",System.currentTimeMillis() - currentTime.get());
currentTime.remove();
sysLog.setExceptionDetail(ThrowableUtil.getStackTrace(e).getBytes());
HttpServletRequest request = RequestHolder.getHttpServletRequest();
sysLogService.save(getUsername(), StringUtils.getBrowser(request), StringUtils.getIp(request), (ProceedingJoinPoint)joinPoint, sysLog);
}
public String getUsername() {
try {
return SecurityUtils.getCurrentUsername();
}catch (Exception e){
return "";
}
}
}
三、注解的作用及自定义注解实现方式
3.1 作用
- 提供元数据信息:注解可以为程序元素(如类、方法、字段)添加元数据信息,比如作者、版本号、创建日期等。这些信息可以被工具或框架读取和利用,用于生成文档、做版本控制、自动化处理等。
- 辅助编译检查 :注解可以帮助编译器进行额外的静态检查和验证。例如,使用
@Override
注解可以确保某个方法重写了父类的方法,如果不满足条件则会产生编译错误。 - 配置和触发特定行为 :注解可以用于配置和触发特定的行为。例如,Spring框架中的
@Autowired
注解可以自动注入依赖对象;JUnit中的@Test
注解用于标记测试方法等。 - 代码生成和处理:注解可以用于代码生成和处理工具。例如,编写代码生成器时,可以使用注解来定义生成代码的规则和逻辑,从而简化代码生成过程。
- 运行时处理:注解可以在程序运行时通过反射机制获取,并根据注解的信息来做特定的处理。例如,使用注解实现权限控制、日志记录等功能。
3.2 实现方式一:AOP
切面AOP编码3板斧:
- 创建切面类(Aspect):首先要创建一个切面类,其中包含了通知(Advice)以及定义切点(Pointcut)的逻辑。
- 定义切点(Pointcut):在切面类中定义切点,它决定了哪些连接点(Join Point)会被通知所影响。
- 编写通知并与切点整合:在切面类中编写不同类型的通知(Before、After、Around 等),然后将这些通知与切点进行整合,以实现对目标方法的增强操作。
示例:如第二节eladmin实现日志记录就是通过AOP的方式
3.3 方式二: 拦截器
- 创建拦截器:创建一个拦截器来检查方法是否被自定义注解标注,如果是,则执行相应的逻辑。
- 注册拦截器:在Spring配置中注册这个拦截器,以便它能够工作。
示例:
- 首先,需要定义一个自定义注解。假设我们要创建一个用于权限验证的注解
@CheckPermission
。
java
@Target(ElementType.METHOD) // 注解适用于方法
@Retention(RetentionPolicy.RUNTIME) // 注解在运行时有效
public @interface CheckPermission {
String value() default ""; // 该注解包含一个名为value的元素,可以在使用注解时指定
}
- 接下来,创建一个拦截器来检查方法是否被
@CheckPermission
注解标注,如果是,则执行相应的权限检查逻辑。
java
public class PermissionInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 检查方法是否有@CheckPermission注解
CheckPermission checkPermission = method.getAnnotation(CheckPermission.class);
if (checkPermission != null) {
// 获取注解的value值,进行权限验证逻辑
String permissionValue = checkPermission.value();
// 这里添加权限验证的逻辑
// 如果权限验证不通过,可以返回false或者抛出异常
}
}
return true; // 如果没有注解或者权限验证通过,则继续执行
}
}
- 最后,你需要在Spring配置中注册这个拦截器,以便它能够工作。如果你使用的是Spring MVC,则可以在配置类中添加如下配置:
java
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new PermissionInterceptor());
}
}