温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Spring框架中的AOP是如何工作的

发布时间:2025-12-14 06:09:07 来源:亿速云 阅读:93 作者:小樊 栏目:编程语言

Spring AOP 的工作机制概览 Spring AOP 基于代理模式运行时把横切逻辑织入目标对象:当从容器获取的 Bean 被调用时,实际执行的是代理对象的方法调用,代理在合适的连接点切点匹配执行相应的通知,从而实现对业务代码的增强。Spring AOP 以方法为连接点,采用JDK 动态代理CGLIB字节码增强来创建代理,默认优先使用 JDK 动态代理,目标类未实现接口时自动切换到 CGLIB。与 AspectJ 的编译期/类加载期织入不同,Spring AOP 属于运行期织入,更轻量、易用,适合大多数业务横切场景(如日志、事务、安全)。

核心概念与代理选择

  • 关键术语
    • 切面 Aspect:封装横切关注点的模块(由切点 + 通知组成)。
    • 连接点 Join point:程序执行点,在 Spring AOP 中主要是方法执行
    • 切点 Pointcut:匹配连接点的表达式,用于决定在哪些方法上应用通知。
    • 通知 Advice:在连接点执行的动作,含**@Before、@After、@AfterReturning、@AfterThrowing、@Around**。
    • 目标对象 Target:被增强的原始对象,最终被代理包裹。
    • 代理 Proxy:由 Spring 在运行时创建,拦截方法调用并织入通知逻辑。
  • 代理机制与选择
    • JDK 动态代理:基于接口,使用 java.lang.reflect.ProxyInvocationHandler,要求目标类实现接口。
    • CGLIB 动态代理:基于继承,生成目标类的子类并重写方法,可代理无接口类;无法代理 final 类/方法
    • 选择规则:默认优先 JDK 动态代理;若目标类未实现接口,则使用 CGLIB;可通过配置强制使用 CGLIB(见下文示例)。

从 Bean 创建到方法拦截的流程

  • 启动与注册:容器启动时,启用 AOP(如 @EnableAspectJAutoProxy),解析所有 @Aspect 切面,注册其中的 Pointcut、Advice,并包装为 Advisor(通知器)。
  • 代理决策:对每个候选 Bean,AOP 基础设施(如 AbstractAutoProxyCreator 及其子类)作为 BeanPostProcessor 在初始化后介入,依据 ClassFilter/MethodMatcher 匹配适用的 Advisor,若匹配到则创建代理。
  • 调用拦截:外部调用 Bean 的方法时,调用被代理拦截,按 Advisor 顺序组装为拦截器链执行;其中 环绕通知 Around 拥有控制权,可决定是否及何时调用 proceed() 进入目标方法;其他通知在指定时机前后执行。

通知执行顺序与环绕通知控制

  • 正常返回时的顺序
    • @Around(before)@Before → 目标方法 → @AfterReturning@After@Around(after)
  • 抛出异常时的顺序
    • @Around(before)@Before → 目标方法 → @AfterThrowing@After@Around(after)
  • 关键点
    • @After@AfterReturning/@AfterThrowing 的区别:前者在任何退出路径都会执行;后两者仅在正常返回/异常抛出时执行。
    • @Around 最强大,可控制是否执行目标方法、修改入参与返回值、捕获并处理异常,是实现耗时统计、权限校验、缓存等横切逻辑的常见选择。

快速上手与常见注意事项

  • 快速上手(Spring Boot)
    • 引入依赖:
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
      
    • 启用 AOP(如未自动启用):在配置类添加 @EnableAspectJAutoProxy
    • 定义切面与切点/通知(示例):
      @Aspect @Component
      public class LogAspect {
        @Pointcut("execution(* com.example.service.*.*(..))")
        void pc() {}
      
        @Around("pc()")
        public Object around(ProceedingJoinPoint pjp) throws Throwable {
          long start = System.currentTimeMillis();
          try {
            return pjp.proceed();
          } finally {
            System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms");
          }
        }
      }
      
  • 常见注意事项
    • Spring AOP 仅拦截外部方法调用(通过代理进入的调用);同一类内方法自调用通常不会被增强,如需增强请重构或通过 AopContext.currentProxy() 等方式绕行。
    • 无法代理 final 类/方法;无接口类将使用 CGLIB
    • 强制使用 CGLIB:在配置类使用 @EnableAspectJAutoProxy(proxyTargetClass = true)
向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI