大家好,我们又见面了,我是你们的朋友全栈君。
本文将详细探讨AOP(面向切面编程)的基本概念、基本应用以及日志管理的实际操作。掌握了这些内容,你将轻松运用AOP技术。
一、基本概念
以下是AOP的一些核心概念:
Aspect(切面)
切面是将关注点模块化的方式,它包括通知和切点。切面定义了其功能的内容、执行的时机和地点。例如,事务管理和日志记录可以视为切面。
Join point(连接点)
连接点是程序执行中的一个点,如方法调用或异常处理。
Advice(通知)
通知是在特定连接点上执行的动作。
Pointcut(切点)
切点是一个匹配连接点的表达式。通知与切点表达式关联,并在匹配的连接点上执行。Spring使用AspectJ切点表达式语言作为默认。
Introduction(引入)
引入允许为类型添加新的方法或字段。Spring AOP允许你为被建议的对象引入新的接口,例如,使bean实现IsModified接口以简化缓存。
Target object(目标对象)
目标对象是被一个或多个切面通知的对象。由于Spring AOP使用运行时代理实现,因此目标对象始终是代理对象。
AOP proxy(AOP代理)
AOP代理是由AOP框架创建的对象,用于实现切面契约。Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
Weaving(织入)
织入是将通知添加到目标类连接点的过程,可以在编译时、加载时或运行时完成。
Spring支持以下五种类型的通知:
通知的执行顺序如下:
![基于SpringBoot使用AOP技术实现操作日志管理[通俗易懂]](https://img.php.cn/upload/article/001/503/042/175245997977775.jpg)
![基于SpringBoot使用AOP技术实现操作日志管理[通俗易懂]](https://img.php.cn/upload/article/001/503/042/175245997925594.jpg)
后续的基本应用部分将展示如何实现这些通知,并演示它们的执行顺序。
二、基本应用
下面是声明通知的示例代码,你可以复制并运行以验证通知的执行顺序:
@Aspect
public class Test {
private static int step = 0;
<pre class="brush:php;toolbar:false;"><code>@Pointcut("@annotation(com.chenyanwu.erp.erpframework.annotation.Log)")
private void operation() {}
@Before("operation()")
public void doBeforeTask() {
System.out.println(++step + " 前置通知");
}
@After("operation()")
public void doAfterTask() {
System.out.println(++step + " 后置通知");
}
@AfterReturning(pointcut = "operation()", returning = "retVal")
public void doAfterReturnningTask(Object retVal) {
System.out.println(++step + " 返回通知,返回值为:" + retVal.toString());
}
@AfterThrowing(pointcut = "operation()", throwing = "ex")
public void doAfterThrowingTask(Exception ex) {
System.out.println(++step + " 异常通知,异常信息为:" + ex.getMessage());
}
@Around("operation()")
public Object doAroundTask(ProceedingJoinPoint pjp) {
String methodname = pjp.getSignature().getName();
Object result = null;
try {
// 前置通知
System.out.println("目标方法" + methodname + "开始,参数为" + Arrays.asList(pjp.getArgs()));
// 执行目标方法
result = pjp.proceed();
// 返回通知
System.out.println("目标方法" + methodname + "执行成功,返回" + result);
} catch (Throwable e) {
// 异常通知
System.out.println("目标方法" + methodname + "抛出异常: " + e.getMessage());
}
// 后置通知
System.out.println("目标方法" + methodname + "结束");
return result;
}}
注意切入点的表达式格式:
三、日志管理实战
基于对基本应用的理解,我们直接展示日志管理的实现代码:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
String value() default "";
}@Aspect
@Order(5)
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(LogAspect.class);
@Autowired
private ErpLogService logService;
@Autowired
ObjectMapper objectMapper;
private ThreadLocal<Date> startTime = new ThreadLocal<Date>();</p><pre class="brush:php;toolbar:false;"><code>@Pointcut("@annotation(com.chenyanwu.erp.erpframework.annotation.Log)")
public void pointcut() {}
@Before("pointcut()")
public void doBefore(JoinPoint joinPoint) {
startTime.set(new Date());
}
@AfterReturning(pointcut = "pointcut()", returning = "rvt")
public void doAfter(JoinPoint joinPoint, Object rvt) throws Exception {
handleLog(joinPoint, null, rvt);
}
@AfterThrowing(pointcut = "pointcut()", throwing = "e")
public void doAfter(JoinPoint joinPoint, Exception e) throws Exception {
handleLog(joinPoint, e, null);
}
@Async
private void handleLog(final JoinPoint joinPoint, final Exception e, Object rvt) throws Exception {
Method method = getMethod(joinPoint);
Log log = getAnnotationLog(method);
if (log == null) {
return;
}
Date now = new Date();
ErpLog erpLog = new ErpLog();
erpLog.setErrorCode(0);
erpLog.setIsDeleted(0);
HttpServletRequest request = ToolUtil.getRequest();
erpLog.setType(ToolUtil.isAjaxRequest(request) ? "Ajax请求" : "普通请求");
erpLog.setTitle(log.value());
erpLog.setHost(request.getRemoteHost());
erpLog.setUri(request.getRequestURI().toString());
erpLog.setHttpMethod(request.getMethod());
erpLog.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
Object[] args = joinPoint.getArgs();
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
if (args != null && paramNames != null) {
StringBuilder params = new StringBuilder();
params = handleParams(params, args, Arrays.asList(paramNames));
erpLog.setParams(params.toString());
}
String retString = JsonUtil.bean2Json(rvt);
erpLog.setResponseValue(retString.length() > 5000 ? JsonUtil.bean2Json("请求参数数据过长不与显示") : retString);
if (e != null) {
erpLog.setErrorCode(1);
erpLog.setErrorMessage(e.getMessage());
}
Date stime = startTime.get();
erpLog.setStartTime(stime);
erpLog.setEndTime(now);
erpLog.setExecuteTime(now.getTime() - stime.getTime());
erpLog.setUsername(MySysUser.loginName());
HashMap<String, String> browserMap = ToolUtil.getOsAndBrowserInfo(request);
erpLog.setOperatingSystem(browserMap.get("os"));
erpLog.setBrower(browserMap.get("browser"));
erpLog.setId(IdUtil.simpleUUID());
logService.insertSelective(erpLog);
}
private Log getAnnotationLog(Method method) {
if (method != null) {
return method.getAnnotation(Log.class);
}
return null;
}
private Method getMethod(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method;
}
return null;
}
private StringBuilder handleParams(StringBuilder params, Object[] args, List paramNames) throws JsonProcessingException {
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Map) {
Set set = ((Map) args[i]).keySet();
List list = new ArrayList();
List paramList = new ArrayList();
for (Object key : set) {
list.add(((Map) args[i]).get(key));
paramList.add(key);
}
return handleParams(params, list.toArray(), paramList);
} else {
if (args[i] instanceof Serializable) {
Class<?> aClass = args[i].getClass();
try {
aClass.getDeclaredMethod("toString", new Class<?>[]{null});
params.append(" ").append(paramNames.get(i)).append(": ").append(objectMapper.writeValueAsString(args[i]));
} catch (NoSuchMethodException e) {
params.append(" ").append(paramNames.get(i)).append(": ").append(objectMapper.writeValueAsString(args[i].toString()));
}
} else if (args[i] instanceof MultipartFile) {
MultipartFile file = (MultipartFile) args[i];
params.append(" ").append(paramNames.get(i)).append(": ").append(file.getName());
} else {
params.append(" ").append(paramNames.get(i)).append(": ").append(args[i]);
}
}
}
return params;
}}
@Log("新增学生")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public ResultBean<String> create(@RequestBody @Validated ErpStudent item) {
if(service.insertSelective(item) == 1) {
insertErpSFamilyMember(item);
return new ResultBean<String>("");
}
return new ResultBean<String>(ExceptionEnum.BUSINESS_ERROR, "新增学生异常!", "新增失败!", "");
}在执行业务操作后,日志将被写入数据库。你可以通过界面查询日志记录:
![基于SpringBoot使用AOP技术实现操作日志管理[通俗易懂]](https://img.php.cn/upload/article/001/503/042/175245998028735.jpg)
完整的日志管理代码可在GitHub上获取:https://www.php.cn/link/201d7a97aad11bd86a94e02edd95bebb
发布者:全栈程序员栈长,转载请注明出处:https://www.php.cn/link/4b73c0453607648d723ab59b5905f61d
原文链接:https://www.php.cn/link/c8377ad2a50fb65de28b11cfc628d75c
以上就是基于SpringBoot使用AOP技术实现操作日志管理[通俗易懂]的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号