本文目录导读:

我来详细讲解Java中实现日志切面的几种常见方式。
基于Spring AOP的实现
1 Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2 自定义日志注解
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {
/**
* 操作模块
*/
String module() default "";
/**
* 操作类型
*/
String operation() default "";
/**
* 是否记录请求参数
*/
boolean recordParams() default true;
/**
* 是否记录返回结果
*/
boolean recordResult() default false;
}
3 日志切面实现
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.UUID;
@Slf4j
@Aspect
@Component
public class LogAspect {
@Autowired
private ObjectMapper objectMapper;
@Pointcut("@annotation(com.example.demo.annotation.LogAnnotation)")
public void logPointcut() {
}
/**
* 环绕通知:最灵活的增强方式
*/
@Around("logPointcut()")
public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);
// 生成请求ID
String requestId = UUID.randomUUID().toString().replace("-", "");
try {
// 记录请求日志
if (logAnnotation.recordParams()) {
logRequest(joinPoint, logAnnotation, requestId);
}
// 执行目标方法
Object result = joinPoint.proceed();
// 记录响应日志
long costTime = System.currentTimeMillis() - startTime;
logResponse(result, logAnnotation, requestId, costTime);
return result;
} catch (Throwable e) {
// 记录异常日志
logError(e, joinPoint, requestId, System.currentTimeMillis() - startTime);
throw e;
}
}
/**
* 前置通知
*/
@Before("logPointcut()")
public void beforeLog(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
log.info("【前置通知】开始执行方法: {}", signature.getMethod().getName());
}
/**
* 后置通知(无论是否异常都会执行)
*/
@After("logPointcut()")
public void afterLog(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
log.info("【后置通知】方法执行完成: {}", signature.getMethod().getName());
}
/**
* 正常返回通知
*/
@AfterReturning(pointcut = "logPointcut()", returning = "result")
public void afterReturningLog(JoinPoint joinPoint, Object result) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
log.info("【返回通知】方法: {}, 返回值: {}",
signature.getMethod().getName(),
result != null ? result.toString() : "null");
}
/**
* 异常通知
*/
@AfterThrowing(pointcut = "logPointcut()", throwing = "exception")
public void afterThrowingLog(JoinPoint joinPoint, Exception exception) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
log.error("【异常通知】方法: {}, 异常: {}",
signature.getMethod().getName(),
exception.getMessage(),
exception);
}
/**
* 记录请求日志
*/
private void logRequest(ProceedingJoinPoint joinPoint, LogAnnotation logAnnotation, String requestId) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取HTTP请求信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes != null ? attributes.getRequest() : null;
LogEntry logEntry = new LogEntry();
logEntry.setRequestId(requestId);
logEntry.setTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
logEntry.setModule(logAnnotation.module());
logEntry.setOperation(logAnnotation.operation());
logEntry.setClassName(joinPoint.getTarget().getClass().getName());
logEntry.setMethodName(signature.getMethod().getName());
logEntry.setParams(Arrays.toString(joinPoint.getArgs()));
if (request != null) {
logEntry.setIp(request.getRemoteAddr());
logEntry.setUrl(request.getRequestURL().toString());
logEntry.setHttpMethod(request.getMethod());
logEntry.setUserAgent(request.getHeader("User-Agent"));
}
log.info("请求日志: {}", toJsonString(logEntry));
}
/**
* 记录响应日志
*/
private void logResponse(Object result, LogAnnotation logAnnotation, String requestId, long costTime) {
if (logAnnotation.recordResult()) {
LogEntry logEntry = new LogEntry();
logEntry.setRequestId(requestId);
logEntry.setTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
logEntry.setCostTime(costTime + "ms");
logEntry.setResult(result != null ? toJsonString(result) : "null");
log.info("响应日志: {}", toJsonString(logEntry));
} else {
log.info("方法执行耗时: {}ms", costTime);
}
}
/**
* 记录异常日志
*/
private void logError(Throwable e, ProceedingJoinPoint joinPoint, String requestId, long costTime) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
LogEntry logEntry = new LogEntry();
logEntry.setRequestId(requestId);
logEntry.setTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
logEntry.setClassName(joinPoint.getTarget().getClass().getName());
logEntry.setMethodName(signature.getMethod().getName());
logEntry.setErrorType(e.getClass().getName());
logEntry.setErrorMessage(e.getMessage());
logEntry.setCostTime(costTime + "ms");
log.error("异常日志: {}", toJsonString(logEntry), e);
}
/**
* 对象转JSON
*/
private String toJsonString(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (Exception e) {
log.error("JSON转换异常", e);
return obj != null ? obj.toString() : "null";
}
}
}
4 日志实体类
import lombok.Data;
@Data
public class LogEntry {
private String requestId; // 请求ID
private String time; // 时间
private String module; // 模块
private String operation; // 操作
private String className; // 类名
private String methodName; // 方法名
private String params; // 参数
private String ip; // IP地址
private String url; // URL
private String httpMethod; // HTTP方法
private String userAgent; // 浏览器信息
private String result; // 返回结果
private String errorType; // 异常类型
private String errorMessage; // 异常信息
private String costTime; // 耗时
}
5 使用示例
@RestController
@RequestMapping("/api/user")
@Slf4j
public class UserController {
@PostMapping("/create")
@LogAnnotation(module = "用户管理", operation = "创建用户", recordParams = true, recordResult = false)
public Result createUser(@RequestBody UserDTO userDTO) {
// 业务逻辑
log.info("创建用户: {}", userDTO);
return Result.success();
}
@GetMapping("/query/{id}")
@LogAnnotation(module = "用户管理", operation = "查询用户", recordParams = true, recordResult = true)
public Result queryUser(@PathVariable Long id) {
// 业务逻辑
User user = new User();
user.setId(id);
user.setName("张三");
return Result.success(user);
}
}
基于AspectJ的实现(非Spring环境)
1 切面定义
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class AspectJLogAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePointcut() {
}
@Around("servicePointcut()")
public Object aroundService(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
String methodName = joinPoint.getSignature().toShortString();
System.out.println("[AspectJ] 开始执行: " + methodName);
System.out.println("[AspectJ] 参数: " + Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - start;
System.out.println("[AspectJ] 执行完成: " + methodName + ", 耗时: " + elapsedTime + "ms");
return result;
}
}
2 aop.xml配置
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<include within="com.example.service.*"/>
</weaver>
<aspects>
<aspect name="com.example.aspect.AspectJLogAspect"/>
</aspects>
</aspectj>
基于动态代理的实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class LogProxy implements InvocationHandler {
private Object target;
public LogProxy(Object target) {
this.target = target;
}
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LogProxy(target)
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
System.out.println("[动态代理] 开始执行: " + methodName);
System.out.println("[动态代理] 参数: " + Arrays.toString(args));
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long elapsedTime = System.currentTimeMillis() - start;
System.out.println("[动态代理] 执行完成: " + methodName + ", 耗时: " + elapsedTime + "ms");
System.out.println("[动态代理] 返回结果: " + result);
return result;
}
}
// 使用示例
public class ProxyDemo {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
UserService proxy = LogProxy.createProxy(userService);
proxy.createUser("张三");
proxy.getUser(1L);
}
}
最佳实践建议
1 性能优化
@Aspect
@Component
public class LogAspectOptimized {
// 使用异步日志记录,避免影响主流程性能
@Autowired
private AsyncLogger asyncLogger;
@Around("@annotation(logAnnotation)")
public Object aroundLog(ProceedingJoinPoint joinPoint, LogAnnotation logAnnotation) throws Throwable {
long startTime = System.currentTimeMillis();
String requestId = UUID.randomUUID().toString();
try {
// 异步记录请求日志
if (logAnnotation.recordParams()) {
asyncLogger.logRequest(requestId, joinPoint, logAnnotation);
}
Object result = joinPoint.proceed();
// 异步记录响应日志
long costTime = System.currentTimeMillis() - startTime;
asyncLogger.logResponse(requestId, result, logAnnotation, costTime);
return result;
} catch (Throwable e) {
asyncLogger.logError(requestId, joinPoint, e, System.currentTimeMillis() - startTime);
throw e;
}
}
}
2 日志级别动态控制
@Slf4j
@Aspect
@Component
public class DynamicLogLevelAspect {
@Around("@annotation(logAnnotation)")
public Object dynamicLog(ProceedingJoinPoint joinPoint, LogAnnotation logAnnotation) throws Throwable {
if (log.isDebugEnabled()) {
log.debug("详细日志记录...");
} else if (log.isInfoEnabled()) {
log.info("基本信息记录...");
}
return joinPoint.proceed();
}
}
3 启用Spring AOP
@SpringBootApplication
@EnableAspectJAutoProxy // 启用AOP
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
实现日志切面的主要方式:
- Spring AOP - 最常用,简单易用,适合Spring项目
- AspectJ - 功能更强大,支持编译时织入
- 动态代理 - 轻量级,适合简单场景
建议使用Spring AOP方式,配合自定义注解实现灵活控制。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播