Spring AOP面向切面编程-从注解到实战
目录一、学习背景与目标1.1 学习背景1.2 学习目标二、核心概念与原理2.1 AOP 是什么2.2 五大核心概念2.3 AOP 的底层原理2.4 五种通知类型三、逻辑图谱与对比3.1 AOP 实现技术图谱3.2 注解 AOP vs XML AOP3.3 决策流程图四、核心详解4.1 基础环境搭建4.2 最标准注解 AOP 模板4.3 JoinPoint vs ProceedingJoinPoint4.4 Pointcut 表达式详解execution —— 最常用按方法签名匹配within —— 按类匹配更快annotation —— 按注解匹配最灵活推荐用于自定义注解场景this / target —— 按代理对象类型匹配bean —— 按 Spring Bean 名称匹配组合表达式4.5 多切面排序4.6 XML 配置 AOP传统方式了解即可五、四大实战案例5.1 案例自定义注解实现接口日志切面最实战5.2 案例接口性能监控 慢请求告警5.3 案例注解方式实现权限校验5.4 案例方法重试切面六、常见坑与最佳实践6.1 易错点清单6.2 内部调用自注入解决方案6.3 最佳实践清单七、总结与学习路线图7.1 知识点提炼核心口诀核心要点速览自检清单7.2 反思与后续规划推荐延伸阅读一句话读懂AOP 让你在不修改源码的前提下给方法批量添加「前置检查」「后置日志」「异常兜底」等能力——就像在公司流程里插入一道审批节点而不用每个部门单独改流程。 适合人群有 Spring Boot 项目经验、了解 IOC/DI 的 Java 开发者 难度等级⭐⭐⭐☆☆中等 ⏱阅读时长约 18 分钟 前置知识Spring Boot 基础、Java 反射基础、代理模式概念一、学习背景与目标1.1 学习背景想象一个真实的项目你需要在每个接口方法里加上日志记录// 改造前——干干净净 public Order getOrder(Long id) { return orderMapper.selectById(id); } ​ // 改造后——每个方法都要加这段 public Order getOrder(Long id) { log.info(调用 getOrder, 参数: id{}, id); // ← 新增 long start System.currentTimeMillis(); try { Order result orderMapper.selectById(id); log.info(getOrder 返回: {}, 耗时: {}ms, result, System.currentTimeMillis() - start); return result; } catch (Exception e) { log.error(getOrder 异常: , e); // ← 新增 throw e; } }一个接口改完还行。十个接口呢五十个呢每一处都是复制粘贴改个日志格式要跑遍全项目。不止日志还有权限校验每个接口方法前查用户角色限流每个接口前查调用次数缓存每个查询前查缓存查询后写缓存事务每个 Service 方法前开启事务结束后提交/回滚性能监控每个方法记录耗时上报这些需求有一个共同点和核心业务无关但散落在各处重复实现。AOP 就是来解决这个问题的。它把这类「横切关注点」抽取成独立模块然后声明式地插入到业务方法的「前置」「后置」「异常」等位置。1.2 学习目标读完本篇后你能做到✅ 理解 AOP 的核心概念切面、连接点、切入点、通知✅ 掌握注解驱动的 AOP 写法最主流、最实用✅ 掌握声明式 AOP的 XML 配置写法✅ 用 AOP 实现三个实战场景日志切面 性能监控 权限校验✅ 知道JoinPoint / ProceedingJoinPoint 的用法区别✅ 知道执行顺序的坑和怎么排二、核心概念与原理2.1 AOP 是什么定义AOPAspect-Oriented Programming面向切面编程是一种编程范式它把多个类中重复的「横切逻辑」抽取成独立的模块切面然后通过动态代理技术在运行时/编译期织入到目标方法执行链中。和 OOP 的区别OOP纵向UserService → OrderService → ProductService ↓ ↓ ↓ 日志代码 日志代码 日志代码 ← 重复 ​ AOP横向 日志切面 ───→ 织入每一个 Service 方法 权限切面 ───→ 织入特定 Controller 方法 事务切面 ───→ 织入所有 Service 方法一句话区分OOP 是竖着切AOP 是横着切。OOP 解决代码复用AOP 解决横切关注点的复用。2.2 五大核心概念概念英文类比一句话理解切面Aspect一个插件模块把我要做的事和我要往哪插打包在一起连接点Join Point所有可能下刀的位置方法调用、异常抛出等都是连接点切入点Pointcut具体下刀的位置哪些方法需要增强的条件表达式通知Advice下刀后做什么前置/后置/环绕/异常/最终五种织入Weaving把刀装上去的过程代理对象生成的过程一张图理清关系项目中的所有方法 → 这些都是 JoinPoint │ │ Pointcut 表达式筛选 ▼ 只匹配到的 TargetMethod │ │ Advice 定义怎么增强 ▼ Aspect Pointcut Advice → 织入生成代理对象2.3 AOP 的底层原理AOP 的底层实现分两种1. JDK 动态代理默认条件目标类实现了至少一个接口 原理运行时生成一个实现了相同接口的代理对象2. CGLIB 代理当目标类没有实现接口时条件目标类没有实现接口或者配置 proxyTargetClasstrue 原理运行时生成目标类的子类作为代理对象Spring Boot 2.x 默认行为优先 CGLIBspring.aop.proxy-target-classtrue因为绝大多数业务类都没有实现接口CGLIB 适配更广。// JDK 代理 public class JdkProxy implements InvocationHandler { private Object target; Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前置增强 Object result method.invoke(target, args); // 后置增强 return result; } } ​ // CGLIB 代理 public class CglibProxy implements MethodInterceptor { Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // 前置增强 Object result proxy.invokeSuper(obj, args); // 后置增强 return result; } }面试考点为什么 Spring AOP 默认用 JDK 动态代理老版本因为 JDK 代理是基于接口的和 Spring 的「面向接口编程」理念一致。后来为了适配更多场景Spring Boot 2.x 默认改成了 CGLIB。2.4 五种通知类型通知类型注解执行时机典型场景前置通知Before目标方法执行前权限校验、参数校验后置通知After目标方法执行后无论是否异常释放资源、记录结果返回通知AfterReturning目标方法正常返回后记录返回值、缓存写入异常通知AfterThrowing目标方法抛出异常后异常告警、回滚操作环绕通知Around包围目标方法可控制执行全过程性能监控、事务、重试执行顺序示意图Around 开始proceed 前 ↓ Before ↓ 目标方法执行 ↓ AfterReturning正常返回 / AfterThrowing异常 ↓ After无论如何都执行 ↓ Around 结束proceed 后注意多个切面可以用Order控制优先级。数值越小前置通知越先执行后置通知越后执行。三、逻辑图谱与对比3.1 AOP 实现技术图谱AOP 的两种主要实现方式 │ ├─ Spring AOP本篇重点 │ ├─ 运行时动态代理JDK / CGLIB │ ├─ 只支持方法级别的连接点 │ └─ 两种配置方式 │ ├─ 注解驱动AspectJ 风格 推荐 │ └─ XML 配置传统 Spring 风格 │ └─ AspectJ纯正的 AOP 框架 ├─ 编译期织入需要特殊编译器 ├─ 支持字段、构造器、方法、属性等所有连接点 └─ 功能更强大但配置更复杂3.2 注解 AOP vs XML AOP对比维度注解驱动AspectJXML 配置声明方式AspectComponentaop:config标签切入点声明Pointcut注解aop:pointcut标签通知声明Before/After/Aroundaop:before/after/around标签可读性⭐⭐⭐⭐⭐代码即文档⭐⭐⭐切面和业务分离灵活性⭐⭐⭐⭐⭐⭐⭐⭐推荐程度✅生产首选❌ 仅适配老项目除非你在维护 Spring XML 配置的老项目否则直接用注解方式。3.3 决策流程图遇到需要批量增强方法的需求 │ ├─ 需求批量给特定的方法加日志/权限/监控 │ └─ → Spring AOP AspectJ 注解本篇方法 │ ├─ 需求要在构造器调用、字段赋值时做拦截 │ └─ → AspectJ 编译期织入如 Spring Configurable │ ├─ 需求对某个类的所有方法做拦截通用的 │ └─ → Spring AOP execution 表达式 │ ├─ 需求对加了某个注解的方法做拦截 │ └─ → Spring AOP annotation 表达式 自定义注解 │ └─ 需求只想对特定包的特定方法名做拦截 └─ → Spring AOP within execution 组合四、核心详解4.1 基础环境搭建!-- Spring Boot 项目AOP 已内置只需引入 starter -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency SpringBootApplication EnableAspectJAutoProxy // Spring Boot 自动配置一般不需要手动加 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }Spring Boot 2.x 默认开启了 AOP 自动配置EnableAspectJAutoProxy不需要手动加。只有在老版本 Spring 项目中才需要。4.2 最标准注解 AOP 模板Aspect // 告诉 Spring 这是一个切面类 Component // 注册到 IOC 容器 Slf4j public class LogAspect { /** * 定义切入点 —— 匹配哪些方法 */ Pointcut(execution(* com.example.service.*.*(..))) public void servicePointcut() {} // 五种通知 Before(servicePointcut()) public void beforeAdvice(JoinPoint joinPoint) { log.info(前置通知: 方法{}, 参数{}, joinPoint.getSignature().getName(), joinPoint.getArgs()); } After(servicePointcut()) public void afterAdvice(JoinPoint joinPoint) { log.info(后置通知: 方法{}, 执行完毕, joinPoint.getSignature().getName()); } AfterReturning(value servicePointcut(), returning result) public void afterReturningAdvice(JoinPoint joinPoint, Object result) { log.info(返回通知: 方法{}, 返回值{}, joinPoint.getSignature().getName(), result); } AfterThrowing(value servicePointcut(), throwing ex) public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) { log.error(异常通知: 方法{}, 异常{}, joinPoint.getSignature().getName(), ex.getMessage()); } Around(servicePointcut()) public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable { long start System.currentTimeMillis(); String methodName pjp.getSignature().getName(); log.info(环绕前置: {}, methodName); Object result; try { result pjp.proceed(); // 执行目标方法关键 log.info(环绕后置: {}, 耗时{}ms, methodName, System.currentTimeMillis() - start); } catch (Throwable t) { log.error(环绕异常: {}, 耗时{}ms, methodName, System.currentTimeMillis() - start); throw t; } return result; } }4.3 JoinPoint vs ProceedingJoinPoint对比JoinPointProceedingJoinPoint适用通知Before,After,AfterReturning,AfterThrowing仅Around能否控制方法执行❌ 不能已固定✅ 能通过proceed()获取方法签名getSignature()同继承自 JoinPoint获取参数getArgs()同获取目标对象getTarget()同修改参数❌✅proceed(Object[] args)一句话记住不控制方法执行就用 JoinPoint要控制执行如修改参数、加 try-catch、重试就用 ProceedingJoinPoint。4.4 Pointcut 表达式详解切入点是 AOP 的灵魂。Spring AOP 支持以下几种表达式execution —— 最常用按方法签名匹配// 语法execution(修饰符 返回值 包.类.方法(参数) throws 异常) // 匹配 Service 包下所有类的所有方法 Pointcut(execution(* com.example.service.*.*(..))) // 匹配 Service 包及子包下所有类的所有方法 Pointcut(execution(* com.example.service..*.*(..))) // 匹配 OrderService 的所有 public 方法 Pointcut(execution(public * com.example.service.OrderService.*(..))) // 匹配 findById 方法不管参数 Pointcut(execution(* com.example.service.*.findById(..))) // 匹配无参数的方法 Pointcut(execution(* com.example.service.*.*()))within —— 按类匹配更快// 匹配 Service 包下的所有 Bean Pointcut(within(com.example.service.*)) // 匹配 OrderService 类 Pointcut(within(com.example.service.OrderService))annotation —— 按注解匹配最灵活推荐用于自定义注解场景// 匹配加了 LogAnnotation 注解的方法 Pointcut(annotation(com.example.annotation.LogAnnotation))this / target —— 按代理对象类型匹配// target目标对象的类型 Pointcut(target(com.example.service.OrderService)) // this代理对象的类型通常和 target 一样除非多层代理 Pointcut(this(com.example.service.OrderService))bean —— 按 Spring Bean 名称匹配// 匹配名称以 Service 结尾的 Bean Pointcut(bean(*Service)) // 匹配 OrderServiceImpl Pointcut(bean(orderServiceImpl))组合表达式// 且、||或、!非 Pointcut(execution(* com.example.service.*.*(..)) !execution(* com.example.service.*.set*(..)))4.5 多切面排序Aspect Component Order(1) // 数值越小优先级越高 public class LogAspect { ... } Aspect Component Order(2) public class PermissionAspect { ... }执行顺序PermissionAspectAround 前Order1先执行 LogAspectAround 前Order2 PermissionAspectBefore LogAspectBefore 目标方法 LogAspectAfterReturning PermissionAspectAfterReturning LogAspectAround 后 PermissionAspectAround 后直观理解Order 值小的切面「包在外面」前置先执行后置后执行。4.6 XML 配置 AOP传统方式了解即可!-- 切面类纯 Java 类不需要 Aspect -- public class XmlLogAspect { public void beforeLog(JoinPoint joinPoint) { log.info(前置日志: {}, joinPoint.getSignature().getName()); } public void afterLog(Object result) { log.info(后置日志: {}, result); } } !-- applicationContext.xml -- aop:config aop:aspect refxmlLogAspect !-- 定义切入点 -- aop:pointcut idservicePointcut expressionexecution(* com.example.service.*.*(..))/ !-- 定义通知 -- aop:before methodbeforeLog pointcut-refservicePointcut/ aop:after-returning methodafterLog pointcut-refservicePointcut returningresult/ /aop:aspect /aop:config五、四大实战案例5.1 案例自定义注解实现接口日志切面最实战 需求描述用一个Loggable注解标注在需要记录完整入参出参耗时的方法上自动输出日志。 实现代码// 1. 自定义注解 Target({ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) public interface Loggable { String value() default ; // 业务描述 boolean logParams() default true; // 是否打印参数 boolean logResult() default true; // 是否打印返回值 } // 2. 切面实现 Aspect Component Slf4j public class LoggableAspect { Pointcut(annotation(com.example.annotation.Loggable)) public void loggablePointcut() {} Around(loggablePointcut()) public Object handle(ProceedingJoinPoint pjp) throws Throwable { // 获取注解信息 MethodSignature signature (MethodSignature) pjp.getSignature(); Loggable loggable signature.getMethod().getAnnotation(Loggable.class); String methodName signature.getDeclaringTypeName() . signature.getName(); String businessDesc loggable.value(); // 构建日志 StringBuilder sb new StringBuilder(); sb.append(【Loggable】).append(businessDesc) .append( | 方法: ).append(methodName); if (loggable.logParams()) { sb.append( | 参数: ).append(Arrays.toString(pjp.getArgs())); } long start System.currentTimeMillis(); try { Object result pjp.proceed(); sb.append( | 耗时: ).append(System.currentTimeMillis() - start).append(ms); if (loggable.logResult()) { sb.append( | 返回: ).append(result); } log.info(sb.toString()); return result; } catch (Throwable t) { sb.append( | 异常: ).append(t.getMessage()); log.error(sb.toString(), t); throw t; } } } // 3. 在业务方法上使用 Service public class OrderService { Loggable(value 查询订单, logResult false) // 不打印返回值可能包含敏感信息 public Order getOrder(Long id) { return orderMapper.selectById(id); } Loggable(value 创建订单) public Order createOrder(CreateOrderRequest request) { // 业务逻辑 return order; } } 输出日志【Loggable】查询订单 | 方法: com.example.service.OrderService.getOrder | 参数: [1001] | 耗时: 23ms✅ 一句话总结自定义注解 annotation切入点 AOP 最灵活、最面试友好的写法。5.2 案例接口性能监控 慢请求告警 需求描述监控所有 Controller 方法的执行时间超过 500ms 的 Method 输出告警日志。 实现代码Aspect Component Slf4j public class PerformanceMonitorAspect { private static final long SLOW_THRESHOLD_MS 500; Pointcut(execution(* com.example.controller.*.*(..))) public void controllerPointcut() {} Around(controllerPointcut()) public Object monitor(ProceedingJoinPoint pjp) throws Throwable { long start System.nanoTime(); try { return pjp.proceed(); } finally { long durationMs (System.nanoTime() - start) / 1_000_000; String methodName pjp.getSignature().toShortString(); if (durationMs SLOW_THRESHOLD_MS) { // 慢请求告警级别 log.warn(⚠️ 慢请求告警: {} | 耗时{}ms 阈值{}ms, methodName, durationMs, SLOW_THRESHOLD_MS); } else { log.debug(性能监控: {} | 耗时{}ms, methodName, durationMs); } } } }✅ 一句话总结Around配合finally块 纳秒计时低成本实现全接口性能监控。5.3 案例注解方式实现权限校验 需求描述用RequireRole(ADMIN)注解来标记需要特定角色的接口无权直接返回 403。 实现代码// 1. 注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String[] value(); // 允许的角色列表 } // 2. 切面 Aspect Component public class RoleCheckAspect { Pointcut(annotation(requireRole)) public void rolePointcut(RequireRole requireRole) {} Before(value rolePointcut(requireRole), argNames joinPoint,requireRole) public void checkRole(JoinPoint joinPoint, RequireRole requireRole) { // 从当前请求上下文中获取用户角色实际项目中从 JWT/Session 取 String currentUserRole SecurityContextHolder.getContext() .getAuthentication().getAuthorities() .stream().findFirst().orElse().getAuthority(); String[] allowedRoles requireRole.value(); boolean hasPermission Arrays.asList(allowedRoles).contains(currentUserRole); if (!hasPermission) { throw new AccessDeniedException(权限不足需要角色: Arrays.toString(allowedRoles)); } } } // 3. 使用 RestController RequestMapping(/admin) public class AdminController { GetMapping(/deleteUser) RequireRole(ADMIN) public Result deleteUser(Long userId) { userService.delete(userId); return Result.success(); } } 要点解析annotation(requireRole)可以同时绑定注解实例到通知参数中优雅地拿到注解上配置的角色数组异常统一由 Spring 的ControllerAdvice处理全局返回 4035.4 案例方法重试切面 需求描述某些外部接口调用偶尔网络超时失败想自动重试 3 次。 实现代码// 1. 注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface Retryable { int maxRetries() default 3; long delayMs() default 100; // 重试间隔 Class? extends Throwable[] retryFor() default {Exception.class}; // 哪些异常需重试 } // 2. 切面 Aspect Component public class RetryAspect { Pointcut(annotation(retryable)) public void retryPointcut(Retryable retryable) {} Around(value retryPointcut(retryable), argNames pjp,retryable) public Object handleRetry(ProceedingJoinPoint pjp, Retryable retryable) throws Throwable { int maxRetries retryable.maxRetries(); long delayMs retryable.delayMs(); Class? extends Throwable[] retryFor retryable.retryFor(); Throwable lastException null; for (int i 0; i maxRetries; i) { try { return pjp.proceed(); } catch (Throwable t) { lastException t; // 判断是否需要重试 boolean shouldRetry Arrays.stream(retryFor) .anyMatch(clazz - clazz.isAssignableFrom(t.getClass())); if (!shouldRetry || i maxRetries) { throw t; // 不重试的异常 或 重试已达上限直接抛出 } log.warn(方法执行失败第{}次重试, 异常{}, i 1, t.getMessage()); if (delayMs 0) { Thread.sleep(delayMs); } } } throw lastException; // 不会走到这里但满足编译器 } } // 3. 使用 Service public class PaymentClient { Retryable(maxRetries 3, delayMs 200, retryFor {SocketTimeoutException.class, ConnectException.class}) public PaymentResponse callExternalApi(PaymentRequest request) { // 调用外部支付网关 return restTemplate.postForObject(url, request, PaymentResponse.class); } }六、常见坑与最佳实践6.1 易错点清单#坑点现象原因✅ 避坑方案1内部方法调用不生效AOP 失效日志没打出来类内部this.method()调用不走代理用AopContext.currentProxy()或注入自身2同一切面内 Before 引用 Around 不存在的 Pointcut启动报错或顺序混乱Pointcut 定义理解不一致每个切面单独定义 Pointcut3Around 忘记调用 proceed()目标方法不执行环绕通知必须手动 proceed模板写在 IDE 里永远不在 Around 里漏proceed()4多切面 Order 未指定执行顺序不可控默认按切面类名排序显式加Order(n)5annotation 表达式里方法参数名不匹配启动失败提示无法解析注解名写法错误确保annotation(变量名)和切面方法参数名一致6Spring Boot 2.x 与 3.x 的 AOP 自动配置差异切面不生效3.x 默认不开启 AOPEnableAspectJAutoProxy显式开启7调用 private 方法切面不生效Spring AOP 只能拦截 public 方法改为 public 方法8切面类没有加 Component切面无效不在 IOC 容器中检查 Aspect Component 组合9静态方法被切面拦截不生效Spring AOP 不支持静态方法改为实例方法或用 AspectJ10代理对象强转失败ClassCastExceptionJDK 代理不能强转为目标类本身面向接口编程或用 CGLIB 代理6.2 内部调用自注入解决方案Service public class OrderService { Autowired private OrderService self; // 注入自身代理 public void outerMethod() { // 错误this.innerMethod() 不触发 AOP // this.innerMethod(); // 正确通过代理对象调用 self.innerMethod(); } Loggable(内部方法) public void innerMethod() { // 业务逻辑 } } 或者使用 EnableAspectJAutoProxy(exposeProxy true) Service public class OrderService { public void outerMethod() { // 暴露当前代理对象 ((OrderService) AopContext.currentProxy()).innerMethod(); } }6.3 最佳实践清单✅优先用自定义注解 annotation切入点——灵活、可配置、面试加分✅所有切面类显式加Order——可读性强顺序明确✅Around 通知里永远不要忘记proceed()——IDE 里建模板自动生成✅Before 中抛出的异常会被 Around 捕获——时序问题时先确认通知执行链✅切入点表达式尽量精确——execution(* com.example.service.*.*(..))比execution(* *..*.*(..))好✅日志切面放在 Order 最后值最大——确保其他切面权限、事务先运行❌不要在 Around 里吞异常——吞了异常上游感知不到❌不要在 AOP 切面里写复杂业务逻辑——切面是横切关注点不是业务层❌不要用 AOP 替代事务注解——Transactional本身就是 AOP不要重复造轮子七、总结与学习路线图7.1 知识点提炼核心口诀切面编程三要素切点通知和切面 execution 最常用annotation 最灵活 Around 要手动 proceedBefore 只能看不能碰 内部调用不生效注入自己才管用核心要点速览维度要点一句话记住概念切面 切入点 通知哪个方法 做什么事切入点execution按签名annotation按注解精确用 execution灵活用 annotation通知5 种记住Around是万能之王能控制执行的全过程代理JDK 基于接口CGLIB 基于继承没有接口就 CGLIB坑内部方法调用不走 AOP注入自己或者 exposeProxy自检清单我能说清楚 AOP 的五大核心概念我能写出一个自定义注解 切面的完整代码我知道 Before、After、Around 的执行顺序和区别我知道 JoinPoint 和 ProceedingJoinPoint 的区别我会用 execution 和 annotation 两种切入点表达式我知道为什么「同一类内部方法调用」AOP 不生效我能用 AOP 实现日志、权限校验、重试三个场景7.2 反思与后续规划本次总结从手写重复日志的痛点引出 AOP理解了它解决的是横切关注点复用问题掌握了注解式 AOP 的标准模板这是最主流的写法通过 4 个实战案例覆盖了日志、监控、权限、重试四大典型场景避坑清单覆盖了 AOP 最常见的 10 个坑尤其是「内部调用不生效」下一步学习路线图阶段一基础 — 本篇已覆盖注解 AOP 5 种通知 execution/annotation ↓ 阶段二深入原理JDK 动态代理 vs CGLIB 源码分析 代理对象创建过程 ↓ 阶段三源码级Transactional 的 AOP 实现 AbstractAutoProxyCreator 解析 ↓ 阶段四架构AOP 策略模式实现业务扩展点 多级缓存 AOP 实现推荐延伸阅读Spring AOP 官方文档 — 最权威的参考资料EnableAspectJAutoProxy 源码解析 — 理解 AOP 自动配置原理Spring AOP 代理对象创建过程 — 源码级解读 JDK 和 CGLIB 的创建差异

相关新闻

技术咨询:针对面向互联网的Fortinet设备的FortiBleed凭据曝光活动

技术咨询:针对面向互联网的Fortinet设备的FortiBleed凭据曝光活动

近期 Bitdefender Labs 发布官方安全预警:名为 FortiBleed 的大规模凭据窃取攻击活动正在全球扩散,本次风险不存在可修复系统漏洞,没有官方补丁能够彻底封堵。截至 6 月 19 日,攻击者已从 194 个国家约 86644 台独立设备中盗取管理…

2026/7/7 4:13:02阅读更多 →
Okbiye|分档适配全类型期刊,AI 一站式搞定普通 / 核心 / SCI 论文撰写

Okbiye|分档适配全类型期刊,AI 一站式搞定普通 / 核心 / SCI 论文撰写

okbiye-免费查重复率aigc检测/开题报告/毕业论文/智能排版/文献综述/期刊论文期刊论文 - Okbiye智能写作https://www.okbiye.com/ai/qklw 对于本科生、硕博研究生以及科研工作者来说,期刊投稿是学业晋升、成果产出的关键一环。但撰写期刊论文的难点始终难以规避&…

2026/7/7 4:13:02阅读更多 →
2026顶尖EMBA对比:五大优质项目综合实力评测榜单

2026顶尖EMBA对比:五大优质项目综合实力评测榜单

一、评测引言随着国内企业全球化布局、数字化转型进程加速,境内外中英双语EMBA项目成为企业创始人、高层管理者进阶深造、拓宽全球视野、搭建高端人脉的核心选择。当前EMBA教育市场项目繁杂,内地985高校头部EMBA与境外优质双语EMBA各有办学特色&#xff…

2026/7/7 4:13:02阅读更多 →
煤炭井下通信链路隐患排查,GN-W10A 测试设备适配矿山复杂网络环境

煤炭井下通信链路隐患排查,GN-W10A 测试设备适配矿山复杂网络环境

煤炭作为核心基础能源,国内各大矿区正持续推进智慧矿山建设,井下安全监测、生产调度等系统,均依托光纤、以太网、E1 线路完成数据传输工作。井下粉尘浓度高、环境湿度大、温差波动明显且存在电磁干扰,多重复杂环境因素&#xff0c…

2026/7/7 5:18:07阅读更多 →
从花屏到丢帧:视频设备晶振选型 6 大致命误区,90% 研发都踩过

从花屏到丢帧:视频设备晶振选型 6 大致命误区,90% 研发都踩过

4K/8K 大屏、HDMI2.1、120Hz 高刷全面普及,石英晶振作为视频设备的时序心脏,微小参数偏差都会直接毁掉画面效果。 很多硬件工程师都遇过这些糟心事:画面频繁撕裂、音画严重不同步、户外拍摄随机丢帧、设备接口间歇性黑屏。 反复排查主控芯片、…

2026/7/7 5:18:07阅读更多 →
Python全栈项目--手机语音远程控制智能家居系统(含嵌入式控制)

Python全栈项目--手机语音远程控制智能家居系统(含嵌入式控制)

Python全栈项目–手机语音远程控制智能家居系统(含嵌入式控制) 一、项目简介 本项目是一个面向智能家居物联网场景的 Python 全栈实战项目。系统支持在手机端网页中点击麦克风输入语音,例如说出“打开客厅主灯”“卧室空调温度调到25”“我…

2026/7/7 5:18:07阅读更多 →
阿兹特克的黄昏:英格兰3-2逆转墨西哥的战术叙事

阿兹特克的黄昏:英格兰3-2逆转墨西哥的战术叙事

导语 费城林肯金融球场的看台上,一位墨西哥老球迷在终场哨响后久久伫立。他手里攥着一件褪色的1986年墨西哥球衣,上面印着"阿兹特克雄鹰"的字样。四十年前,那支墨西哥队在世界杯上让世界颤抖;四十年后,他的国…

2026/7/7 5:18:07阅读更多 →
自媒体爆款流量的底层逻辑:不是内容好,是匹配用户需求

自媒体爆款流量的底层逻辑:不是内容好,是匹配用户需求

我做自媒体好几年了,发现一个很奇怪的现象,很多人特别努力,天天熬夜写文案、剪视频,细节抠得特别到位,内容看着也很专业,可就是没播放、没互动。反倒一些没怎么精心制作的普通博主,随随便便更新…

2026/7/7 5:18:07阅读更多 →
别再被复杂指标忽悠了:一个让账户扭亏为盈的“极简法则”

别再被复杂指标忽悠了:一个让账户扭亏为盈的“极简法则”

引言:为什么你学得越多,亏得越多?在二级市场折腾这么久,你累吗?每天盯盘到眼红,各种战法、指标学了一大堆,大大小小的消息面一个不落,可回头一看账户,还是在缩水。这种“…

2026/7/7 5:13:07阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/7 4:43:43阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/7 2:56:31阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/7 1:03:28阅读更多 →
Acunetix v24.8 深度解析:DAST漏洞扫描器核心原理与DevSecOps实践

Acunetix v24.8 深度解析:DAST漏洞扫描器核心原理与DevSecOps实践

1. 项目概述:Acunetix v24.8 高级版漏洞扫描器深度解析作为一名在网络安全领域摸爬滚打多年的老兵,我深知一款趁手的“兵器”对于安全测试工作意味着什么。今天要聊的,就是Web应用安全测试领域里一个响当当的名字——Acunetix。特别是其v24.8…

2026/7/7 0:02:41阅读更多 →
国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot)

国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot)

国产化信创改造:达梦/人大金仓适配与多数据库兼容方案实战(SpringBoot) 🌐 演示地址:http://ruoyioffice.com | 📦 源码1GitHub:ruoyi-office | 📦 源码2GitCode:ruoyi-o…

2026/7/7 0:02:41阅读更多 →
CentOS 7/8 SSH 连接失败:5步系统性排错流程与决策树

CentOS 7/8 SSH 连接失败:5步系统性排错流程与决策树

CentOS SSH连接故障排查:从基础检查到深度修复的完整指南引言当你尝试通过Xshell或其他SSH客户端连接CentOS服务器时,突然遭遇"Connection refused"或"Connection timed out"的错误提示,这种经历对任何运维人员或开发者来…

2026/7/7 0:02:41阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/7 5:11:21阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/7 5:11:21阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/7 5:11:21阅读更多 →