aop\动态代理什么时候失效?为什么失效?

aop\动态代理什么时候失效?为什么失效?

动态代理失效

一、JDK 动态代理的硬性限制

1. 目标对象没有实现任何接口

public class UserService {  // 没有实现任何接口
    public void save() { ... }
}

// 调用 Proxy.newProxyInstance(classLoader, interfaces, handler)
// ❌ 报错:接口数组为空,无法生成代理

原因:JDK 动态代理生成的代理类会实现传入的接口数组,代理类本身是 java.lang.reflect.Proxy 的子类。如果没有接口,就没有可以“实现”的契约,代理类无法定义任何方法。

结论没有接口 → JDK 动态代理彻底失效,只能改用 CGLIB。

2. 接口中的方法被 finalstatic 修饰(逻辑上无法拦截)

虽然编译期就会报错(接口方法不能是 final),但这里提一下:

  • 接口方法默认 public abstract,JDK 代理可以正常拦截。
  • 但代理对象无法拦截 static 方法(因为静态方法属于类,不属于实例)。
public interface UserMapper {
    static void staticMethod() { ... }  // 代理无法拦截
}

原因InvocationHandler.invoke() 只拦截实例方法调用,静态方法通过 接口名.方法名() 调用,不经过代理对象。

二、CGLIB 动态代理的硬性限制

3. 目标类是 final

public final class UserService {  // final 类
    public void save() { ... }
}

// Enhancer.create() ❌ 报错:无法继承 final 类

原因:CGLIB 通过继承目标类生成子类(代理类)。final 类不能被继承,所以代理创建失败。

4. 目标方法是 final

public class UserService {
    public final void save() { ... }  // final 方法
}

// 代理对象可以创建成功,但调用 save() 时无法拦截

原因:CGLIB 通过覆写(Override)父类方法来实现拦截。final 方法无法被覆写,所以代理会直接调用父类原方法MethodInterceptor 不会被触发。

注意:这种情况不算“完全失效”,而是“部分失效”——代理对象存在,但该方法上的增强逻辑不执行。

5. 目标方法是 private

public class UserService {
    private void save() { ... }  // private 方法
}

原因:子类无法继承 private 方法,更不可能覆写它,所以 CGLIB 无法拦截。

三、JDK 和 CGLIB 共同失效的场景

6. 目标对象本身就是代理对象(代理套代理)

虽然技术上可以嵌套代理,但如果处理不当(比如死循环、类加载器冲突),会导致 Proxy.newProxyInstanceEnhancer.create() 抛出异常。

7. 类加载器不一致(ClassLoader 问题)

JDK 动态代理要求代理类和目标类使用相同的 ClassLoader(或代理类的 ClassLoader 能访问到目标类的接口)。如果传入的 ClassLoader 无法加载目标类,会抛出 IllegalArgumentException

8. InvocationHandlerMethodInterceptor 中未调用 invoke() / intercept()

这不是“代理失效”,而是“增强逻辑失效”——但如果你在拦截器中什么也不做,直接返回 null,代理对象调用方法会返回 null,看起来就像“失效”了。


面试一句话总结

“动态代理失效的本质,是代理技术的前提条件被破坏:JDK 代理要求目标有接口,CGLIB 代理要求目标类和目标方法不能被 final 修饰。任何绕过代理对象的直接调用(如 this. 调用)也属于代理失效。其他情况如类加载器不一致、目标方法是 private/static,都会导致代理无法创建或无法拦截。”

AOP 失效

一、场景一:类内部方法调用(最常见!)

@Service
public class UserService {
    public void methodA() {
        System.out.println("A执行");
        this.methodB(); // ❌ 这里直接调用内部方法
    }
    
    @Transactional
    public void methodB() {
        System.out.println("B执行");
    }
}

现象:调用 methodA() 时,methodB 上的 @Transactional 不生效

原因(底层原理)

  • Spring AOP 生成的是代理对象(假设叫 UserService$$Proxy)。
  • 外部调用 userService.methodA() 时,走的是代理对象 → 代理逻辑生效。
  • 但代理对象内部调用 methodB() 时,this 指向的是原始目标对象(真实实例),而不是代理对象。
  • 所以 methodB() 直接被原始对象调用,绕过了代理,事务等增强自然失效。

解决方案

@Autowired
private UserService self; // 注入自己(代理对象)

public void methodA() {
    self.methodB(); // 走代理,事务生效
}

或者:

((UserService) AopContext.currentProxy()).methodB();

二、场景二:方法不是 public

@Transactional // ❌ 不生效
private void updatePrivate() { ... }

@Transactional // ❌ 不生效(除非特殊配置)
protected void updateProtected() { ... }

原因(底层原理)

  • Spring 默认使用 JDK 动态代理(基于接口)或 CGLIB(基于继承子类)。
  • 无论哪种,代理都只能拦截 public 方法
  • 为什么?因为 JDK 代理只能代理接口中的 public 方法;CGLIB 虽然可以代理 protected,但 Spring 默认配置下为了统一,只拦截 public
  • 更深层:@Transactional 等注解的解析逻辑在 AbstractFallbackTransactionAttributeSource 中,默认忽略非 public 方法

解决方案:将方法改为 public,或启用 proxy-target-class=true + 配置 AspectJ 编译期织入(不推荐)。


三、场景三:代理方式不匹配(接口 vs 实现类)

// Service 实现了接口
public interface UserService { void save(); }
@Service
public class UserServiceImpl implements UserService { ... }

// 但 AOP 配置强制使用 CGLIB,而类又是 final 的
@EnableAspectJAutoProxy(proxyTargetClass = true)

现象:Spring 容器启动失败,报错 “无法创建代理”。

原因

  • 强制 CGLIB 时,Spring 会尝试生成目标类的子类。
  • 如果目标类是 final 的,CGLIB 无法继承 → 代理创建失败。
  • 如果目标方法被 final 修饰,CGLIB 无法覆盖 → 该方法上的 AOP 不生效。

解决方案:避免将 Bean 类或方法声明为 final


四、场景四:循环依赖 + AOP 导致代理失效(罕见但致命)

@Service
public class A {
    @Autowired private B b;
    @Transactional public void aMethod() { ... }
}

@Service
public class B {
    @Autowired private A a;
    @Transactional public void bMethod() { ... }
}

现象:可能启动失败,或事务偶尔不生效。

原因(底层原理)

  • Spring 解决循环依赖时,会使用三级缓存提前暴露 ObjectFactory
  • 如果 Bean 被 AOP 增强,Spring 需要在创建早期就生成代理对象(通过 EarlyBeanReference)。
  • 如果 AOP 的代理对象和最终注入的不是同一个实例(比如 @Async@Transactional 混用),可能导致某个切面失效。

解决方案:使用 @Lazy 延迟注入,或重构代码避免循环依赖。


五、场景五:使用 new 关键字创建对象=

UserService service = new UserService(); // ❌ 不是 Spring Bean
service.save(); // AOP 完全不生效

原因:对象没有经过 Spring 容器管理,没有代理过程,直接就是原始对象。

解决方案:所有需要 AOP 增强的 Bean,必须通过 Spring 容器获取(@AutowiredApplicationContext.getBean())。


六、场景六:目标类没有被 Spring 管理

// 虽然加了 @Service,但包扫描没扫到
// 或者 @ComponentScan 没有包含该包路径

现象:Bean 根本没有注册到容器,自然没有代理。

解决方案:检查包扫描路径,或显式使用 @Import / @Bean 注册。


面试终极总结(背下来)

“AOP 失效的本质,是目标方法的调用没有经过代理对象。常见原因有 6 类:

  1. 内部调用this 调用绕过了代理;
  2. 非 public 方法:代理默认只拦截 public
  3. 代理方式冲突final 类/方法无法被 CGLIB 代理;
  4. 循环依赖:代理对象提前暴露导致切面错乱;
  5. 手动 new 对象:脱离 Spring 容器管理;
  6. 包扫描遗漏:Bean 未被注册。

解决思路就是:确保所有增强方法走代理对象,且满足代理技术的前提条件。

posted @ 2026-07-19 14:02  deyang  阅读(3)  评论(0)    收藏  举报