2026/4/3 8:38:41
网站建设
项目流程
遵义市做网站的电话,公司网页制作视频教程,wordpress的vps建站流程,云南做网站哪家便宜现在看具体实现。
一、AOP 到底对什么使用#xff1f;
AOP主要针对横切关注点#xff0c;这些代码通常#xff1a;
分散在各处#xff1a;相同的逻辑出现在多个地方与业务逻辑无关#xff1a;不是核心业务#xff0c;但又必须存在容易忘记或重复#xff1a;如每个方法…现在看具体实现。一、AOP 到底对什么使用AOP主要针对横切关注点这些代码通常分散在各处相同的逻辑出现在多个地方与业务逻辑无关不是核心业务但又必须存在容易忘记或重复如每个方法都要加日志典型应用场景// 没有AOP时这些代码会重复出现在很多方法中publicclassService{publicvoidcreateOrder(Orderorder){// 1. 记录日志重复代码log.info(开始创建订单: {},order);// 2. 权限检查重复代码if(!hasPermission()){thrownewSecurityException();}// 3. 性能监控开始重复代码longstartSystem.currentTimeMillis();// ---------- 核心业务逻辑 ----------// 验证订单// 计算价格// 保存到数据库// 发送通知// ---------------------------------// 4. 性能监控结束重复代码longcostSystem.currentTimeMillis()-start;log.info(方法执行耗时: {}ms,cost);// 5. 再次记录日志重复代码log.info(订单创建成功: {},order.getId());}// 其他方法也有同样的重复代码...}二、代码层面怎么使用1.Spring AOP 注解方式最常用// 1. 定义切面AspectComponentpublicclassLoggingAspect{// 2. 定义切入点哪些方法需要被增强Pointcut(execution(* com.example.service.*.*(..)))publicvoidserviceMethods(){}// 3. 前置通知在方法执行前执行Before(serviceMethods())publicvoidlogBefore(JoinPointjoinPoint){StringmethodNamejoinPoint.getSignature().getName();Object[]argsjoinPoint.getArgs();log.info(方法 {} 开始执行参数: {},methodName,Arrays.toString(args));}// 4. 后置通知在方法执行后执行AfterReturning(pointcutserviceMethods(),returningresult)publicvoidlogAfterReturning(JoinPointjoinPoint,Objectresult){log.info(方法 {} 执行成功返回值: {},joinPoint.getSignature().getName(),result);}// 5. 异常通知方法抛出异常时执行AfterThrowing(pointcutserviceMethods(),throwingex)publicvoidlogAfterThrowing(JoinPointjoinPoint,Exceptionex){log.error(方法 {} 执行异常: {},joinPoint.getSignature().getName(),ex.getMessage());}// 6. 环绕通知包裹整个方法Around(serviceMethods())publicObjectlogAround(ProceedingJoinPointjoinPoint)throwsThrowable{longstartSystem.currentTimeMillis();try{// 执行原方法ObjectresultjoinPoint.proceed();longcostSystem.currentTimeMillis()-start;log.info(方法 {} 执行耗时: {}ms,joinPoint.getSignature().getName(),cost);returnresult;}catch(Exceptione){longcostSystem.currentTimeMillis()-start;log.error(方法 {} 执行失败耗时: {}ms,joinPoint.getSignature().getName(),cost);throwe;}}}2.更实际的完整示例// 业务服务类 - 保持干净只关注核心逻辑ServicepublicclassOrderService{// 没有日志、权限、事务等代码publicOrdercreateOrder(OrderRequestrequest){// 纯业务逻辑OrderordernewOrder();order.setItems(request.getItems());order.calculateTotal();orderRepository.save(order);returnorder;}publicOrdergetOrder(Longid){returnorderRepository.findById(id).orElseThrow(()-newOrderNotFoundException(id));}}// 权限检查切面AspectComponentpublicclassSecurityAspect{Before(annotation(RequireRole))// 只拦截有RequireRole注解的方法publicvoidcheckPermission(JoinPointjoinPoint){MethodSignaturesignature(MethodSignature)joinPoint.getSignature();RequireRoleannotationsignature.getMethod().getAnnotation(RequireRole.class);StringrequiredRoleannotation.value();if(!currentUser.hasRole(requiredRole)){thrownewAccessDeniedException(需要角色: requiredRole);}}}// 缓存切面AspectComponentpublicclassCacheAspect{Around(annotation(Cacheable))publicObjectcacheResult(ProceedingJoinPointjoinPoint)throwsThrowable{StringcacheKeygenerateCacheKey(joinPoint);// 先查缓存Objectcachedcache.get(cacheKey);if(cached!null){returncached;}// 执行方法ObjectresultjoinPoint.proceed();// 存入缓存cache.put(cacheKey,result,300);// 缓存5分钟returnresult;}}3.自定义注解 AOP非常实用的模式// 自定义注解Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfaceAuditLog{Stringaction();Stringmodule();}// 审计日志切面AspectComponentpublicclassAuditAspect{AutowiredprivateAuditServiceauditService;AfterReturning(annotation(auditLog))publicvoidauditSuccess(JoinPointjoinPoint,AuditLogauditLog){UseruserSecurityContext.getCurrentUser();auditService.logSuccess(user.getId(),auditLog.module(),auditLog.action(),newDate());}AfterThrowing(valueannotation(auditLog),throwingex)publicvoidauditFailure(JoinPointjoinPoint,AuditLogauditLog,Exceptionex){UseruserSecurityContext.getCurrentUser();auditService.logFailure(user.getId(),auditLog.module(),auditLog.action(),ex.getMessage(),newDate());}}// 在业务方法上使用ServicepublicclassUserService{AuditLog(module用户管理,action创建用户)publicUsercreateUser(UserCreateRequestrequest){// 业务逻辑}AuditLog(module用户管理,action删除用户)RequireRole(ADMIN)// 可以组合多个切面publicvoiddeleteUser(LonguserId){// 业务逻辑}}三、切入点表达式详解Pointcut(execution(* com.example.service.*.*(..)))// 1. execution: 最常用的表达式// * : 任意返回类型// com.example.service.* : service包下所有类// .* : 所有方法// (..) : 任意参数Pointcut(annotation(com.example.RequireRole))// 2. 带有特定注解的方法Pointcut(within(org.springframework.stereotype.Service *))// 3. 带有Service注解的类中的所有方法Pointcut(bean(orderService) || bean(userService))// 4. 特定Bean中的方法Pointcut(args(java.lang.String,..))// 5. 第一个参数是String的方法四、实际项目中的应用顺序// 典型的切面执行顺序可通过Order控制AspectOrder(1)// 最先执行publicclassSecurityAspect{// 1. 权限校验}AspectOrder(2)publicclassLoggingAspect{// 2. 日志记录}AspectOrder(3)publicclassTransactionAspect{// 3. 事务管理Spring已有Transactional}AspectOrder(4)publicclassCacheAspect{// 4. 缓存处理}AspectOrder(5)publicclassRetryAspect{// 5. 重试机制Around(annotation(Retryable))publicObjectretry(ProceedingJoinPointjoinPoint)throwsThrowable{intmaxAttempts3;intattempt0;while(attemptmaxAttempts){try{returnjoinPoint.proceed();}catch(Exceptione){attempt;if(attemptmaxAttempts)throwe;Thread.sleep(1000*attempt);// 延迟重试}}returnnull;}}五、最佳实践建议保持切面简单每个切面只做一件事使用自定义注解精确控制哪些方法需要增强注意执行顺序特别是安全、事务等有依赖关系的切面性能考虑切入点表达式要精确避免匹配过多方法异常处理环绕通知要正确传递或转换异常六、调试技巧// 查看AOP是否生效AspectComponentpublicclassDebugAspect{Before(execution(* com.example..*.*(..)))publicvoiddebug(JoinPointjoinPoint){System.out.println(AOP拦截: joinPoint.getTarget().getClass().getSimpleName().joinPoint.getSignature().getName());}}AOP的核心价值在于让关注点分离让业务代码保持干净。当你发现某个非业务逻辑日志、权限、缓存等在多个地方重复出现时就应该考虑使用AOP来解决了。