创建一个枚举的业务类型对象 public enum BusinessType { /** * 新增 * */ INSERT("新增"), /** * 更新 * */ UPDATE("更新"), /** * 删除 * */ DELETE("删除"); /** * 业务类型 * */ public String type; BusinessType(String t
- 创建一个枚举的业务类型对象
public enum BusinessType {
/**
* 新增
* */
INSERT("新增"),
/**
* 更新
* */
UPDATE("更新"),
/**
* 删除
* */
DELETE("删除");
/**
* 业务类型
* */
public String type;
BusinessType(String type) {
this.type = type;
}
}
- 创建一个枚举的业务类型对象
这段代码定义了一个名为BookLog
的Java注解,主要用于方法上。它包含两个元素:title
和businessType
。其中,title
用于提供描述信息,而businessType
则用于指定业务分类,其值来源于BusinessType
枚举。该注解被设计为在运行时可见,可被包含在javadoc中,且如果一个类被这样的注解标注,其子类会继承这个注解。
import com.ds.common.enums.BusinessType;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface BookLog {
/**
* 描述
*/
String title();
/**
* 业务分类
*/
BusinessType businessType();
}
- 创建LogAspect切面类
使用 Spring AOP(面向切面编程)的切面(Aspect)类,主要用于在指定注解@BookLog
标记的方法上执行日志记录操作。这是一种典型的日志切面,可以在方法执行后记录相关信息,也能在方法抛出异常时记录异常信息。
让我详细解释一下这段代码的关键部分:
@Aspect
注解: 表示这是一个切面类,它包含了一些横切关注点(cross-cutting concerns),即在应用中多个模块中共享的功能。这里的关注点是日志记录。@Component
注解: 将这个类声明为一个 Spring 组件,以便 Spring 容器能够管理它。@Pointcut
注解: 定义切点,即指定在哪些方法上执行切面逻辑。在这里,切点是所有被@BookLog
注解标记的方法。@AfterReturning
注解: 定义一个后置通知,在切点方法成功执行后执行。这里的doAfterReturning
方法用于在方法返回后执行日志记录操作,并将日志信息插入数据库。@AfterThrowing
注解: 定义一个异常通知,在切点方法抛出异常后执行。这里的doAfterThrowing
方法用于在方法抛出异常时执行日志记录操作,并将异常信息插入数据库。JoinPoint
参数: 表示连接点,即被拦截的方法。通过JoinPoint
可以获取方法名、参数等信息。@Autowired
注解: 将SysLogMapper
注入到切面类中,以便在日志记录时可以访问数据库。handleLog
方法: 一个私有方法,用于创建SysLog
对象并设置相关信息,如业务类型、类名、方法名、参数值等。
总体来说,这段代码使用了 Spring AOP 技术,通过切面实现了在被 @BookLog
注解标记的方法上进行日志记录的功能。这种方式可以在不修改原有业务逻辑代码的情况下,方便地添加和管理一些横切关注点,如日志记录、性能监控等。
@Aspect
@Component
public class LogAspect {
@Autowired
private SysLogMapper sysLogMapper;
/**
* 切点
*/
@Pointcut(value = "@annotation(com.ds.common.annotation.BookLog)")
public void logPointCut(){
}
/**
* 方法返回后的通知
*/
@AfterReturning(value = "logPointCut() && @annotation(bookLog)", returning = "obj")
public void doAfterReturning(JoinPoint joinPoint, BookLog bookLog, Object obj) {
SysLog sysLog = handleLog(joinPoint, bookLog);
sysLogMapper.insert(sysLog);
}
/**
* 抛出异常后的通知
*/
@AfterThrowing(value = "logPointCut() && @annotation(bookLog)", throwing = "e")
private void doAfterThrowing(JoinPoint joinPoint, BookLog bookLog, Exception e) {
SysLog sysLog = handleLog(joinPoint,bookLog);
StringWriter stackTraceWriter = new StringWriter();
//异常堆栈信息
e.printStackTrace(new PrintWriter(stackTraceWriter, true));
sysLog.setStackTrace(stackTraceWriter.toString());
//错误信息
String errMsg = e.getMessage();
sysLog.setErrMsg(errMsg);
//异常类型
String exceptionName = e.toString();
sysLog.setExceptionName(exceptionName);
sysLogMapper.insert(sysLog);
}
private SysLog handleLog(JoinPoint joinPoint,BookLog bookLog){
SysLog sysLog = new SysLog();
//设置业务类型
sysLog.setTitle(bookLog.title());
sysLog.setBusinessType(bookLog.businessType().type);
//获取代理类
String className = joinPoint.getTarget().getClass().getName();
sysLog.setClassName(className);
//获取方法名称
String methodName = joinPoint.getSignature().getName();
sysLog.setMethodName(methodName);
//获取包名称
String packageName = joinPoint.getSignature().getDeclaringTypeName();
sysLog.setPackageName(packageName);
// 参数值
Object[] argsValue = Arrays.stream(joinPoint.getArgs()).toArray(Object[]::new);
sysLog.setArgsValue(StringUtils.join(argsValue));
// 参数名
String[] argsNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
sysLog.setArgsName(StringUtils.join(argsNames));
return sysLog;
}
}
- 加载bean到Spring容器并开启Aspect
@Slf4j
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true) //开启aspect
public class BookApplication {
- controller中使用
@BookLog(title = "添加栏目",businessType = BusinessType.INSERT),添加了@BookLog的注解的,会自动记录日志
@BookLog(title = "添加栏目",businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public Result add(@RequestBody @Validated Article article, BindingResult bindingResult){
if (bindingResult.hasErrors()){
return Result.failure(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
}
article.setChannelName(channelService.detail(article.getChannelId()).getName());
boolean flag = articleService.save(article);
return Result.success(flag);
}