废话不多说,先上效果
相关代码
1.目录结构
2.核心切面类LogAspect
package com.zwd.cases.config;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.util.UUID;
/**
* @Description 统一切面日志输出
* @Author zhengwd
* @Date 2023/4/11 10:36
**/
@Aspect
@Component
public class LogAspect {
private final static Logger LOG = LoggerFactory.getLogger(LogAspect.class);
/**
* 定义一个切点
*/
@Pointcut("execution(public * com.zwd.*.controller..*Controller.*(..))")
public void controllerPointcut() {}
@Before("controllerPointcut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 日志编号
MDC.put("UUID", UUID.randomUUID() + "");
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Signature signature = joinPoint.getSignature();
String name = signature.getName();
// 打印业务操作
String nameCn = "";
if (name.contains("list") || name.contains("query")) {
nameCn = "查询";
} else if (name.contains("save")) {
nameCn = "保存";
} else if (name.contains("delete")) {
nameCn = "删除";
} else {
nameCn = "操作";
}
// 使用反射,获取业务名称
Class clazz = signature.getDeclaringType();
Field field;
String businessName = "";
try {
field = clazz.getField("BUSINESS_NAME");
if (!StringUtils.isEmpty(field)) {
businessName = (String) field.get(clazz);
}
} catch (NoSuchFieldException e) {
LOG.error("未获取到业务名称");
} catch (SecurityException e) {
LOG.error("获取业务名称失败", e);
}
// 打印请求信息
LOG.info("------------- 【{}】{}开始 -------------", businessName, nameCn);
LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
LOG.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);
LOG.info("远程地址: {}", request.getRemoteAddr());
// 打印请求参数
Object[] args = joinPoint.getArgs();
Object[] arguments = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof ServletRequest
|| args[i] instanceof ServletResponse
|| args[i] instanceof MultipartFile) {
continue;
}
arguments[i] = args[i];
}
// 排除字段,敏感字段或太长的字段不显示
String[] excludeProperties = {"shard"};
PropertyPreFilters filters = new PropertyPreFilters();
PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
excludefilter.addExcludes(excludeProperties);
// 为空的会不打印,但是像图片等长字段也会打印
LOG.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter));
}
@Around("controllerPointcut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
// 排除字段,敏感字段或太长的字段不显示
String[] excludeProperties = {"password", "shard"};
PropertyPreFilters filters = new PropertyPreFilters();
PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
excludefilter.addExcludes(excludeProperties);
LOG.info("返回结果: {}", JSONObject.toJSONString(result, excludefilter));
LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);
return result;
}
}
注意:定义的切点目录需要修改成你项目对应的位置。否则会不生效。
3.UploadController
package com.zwd.cases.controller;
import com.zwd.cases.common.constant.ResponseDto;
import com.zwd.cases.domain.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Description 文件上传
* @Author zhengwd
* @Date 2022/7/21 16:30
**/
@Slf4j
@Controller
@RequestMapping("testup")
public class UploadController {
public static final String BUSINESS_NAME = "文件上传";
/**
* 单个文件上传页面
* @return
*/
@RequestMapping("/list")
public ResponseDto list(User user) {
ResponseDto responseDto = new ResponseDto();
responseDto.setContent(user);
return responseDto;
}
}
此处的 BUSINESS_NAME 属性不能删了,LogAspect类中有使用!!!
4.ResponseDto
package com.zwd.cases.common.constant;
public class ResponseDto<T> {
/**
* 业务上的成功或失败
*/
private boolean success = true;
/**
* 返回码
*/
private String code;
/**
* 返回信息
*/
private String message;
/**
* 返回泛型数据,自定义类型
*/
private T content;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public boolean getSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("ResponseDto{");
sb.append("success=").append(success);
sb.append(", code='").append(code).append('\'');
sb.append(", message='").append(message).append('\'');
sb.append(", content=").append(content);
sb.append('}');
return sb.toString();
}
}
5.User
package com.zwd.cases.domain;
import lombok.Data;
/**
* @Description 用户对象
* @Author zhengwd
* @Date 2023/4/11 14:55
**/
@Data
public class User {
private String name;
private String age;
}
6.pom.xml文件如果没有AOP切面相关的依赖的话,需要添加以下依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.2.9.RELEASE</version>
</dependency>
fastjson依赖,看自己原先项目有没有了,有就不用加
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
请求地址:http://localhost:8080/testup/list?name=zwd&age=1
查看控制台效果:
扩展(以下是人工智能的回复)
问:面向切面编程能做些什么?简单举例下
答:
// 面向切面编程(AOP)能够在不修改原有代码的情况下,通过切面(Aspect)来实现对代码的增强或者横切关注点的分离。常见的应用场景包括日志记录、性能统计、安全控制、事务处理等。下面是一个简单的例子,使用AOP实现对方法执行时间的统计:
// 定义一个切面类,使用@Aspect注解标注
@Aspect
public class TimeAspect {
// 定义一个切点,匹配com.example.demo包及其子包下的所有类的所有方法
@Pointcut("execution(* com.example.demo..*.*(..))")
public void pointcut() {}
// 定义一个前置通知,在目标方法执行前输出开始时间
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("开始执行:" + joinPoint.getSignature() + ",参数:" + Arrays.toString(joinPoint.getArgs()));
joinPoint.getArgs();
}
// 定义一个后置通知,在目标方法执行后输出结束时间和执行时间
@AfterReturning(pointcut = "pointcut()", returning = "result")
public void afterReturning(JoinPoint joinPoint, Object result) {
System.out.println("执行完成:" + joinPoint.getSignature() + ",返回值:" + result);
}
// 定义一个异常通知,在目标方法抛出异常时输出异常信息
@AfterThrowing(pointcut = "pointcut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
System.out.println("执行出错:" + joinPoint.getSignature() + ",异常信息:" + e.getMessage());
}
}
// 在Spring配置文件中开启AspectJ自动代理,并扫描TimeAspect所在的包
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.example.demo.aspect" />
// 在需要统计执行时间的方法上添加@Aspect注解
@Service
public class UserServiceImpl implements UserService {
@Override
@TimeAspect
public void addUser(User user) {
// ...
}
}
完结,撒花~~~
附相关其他日志:idea控制台显示日志规范化并存储控制台输出日志操作