jdk动态代理只能代理public方法,因为接口中方法默认且必须为public abstract,而代理类通过反射调用目标方法时未调用setaccessible(true),导致非public实现方法抛illegalaccessexception。

JDK 动态代理要求目标类实现的接口中,所有被代理的方法必须是 public 的。
为什么只能代理 public 方法
JDK 动态代理基于 java.lang.reflect.Proxy 和 InvocationHandler 实现,其本质是运行时生成一个新类(如 $Proxy2),该类实现目标对象所声明的所有接口。而 Java 规范规定:接口中的方法默认且只能是 public abstract,不允许使用 private、protected 或包级访问修饰符。
因此,即使你在接口里显式写 public void doSomething(),它和不写修饰符效果一样;但若尝试定义 private void xxx(),编译器会直接报错——接口根本不允许这种写法。
对目标类实现方法的访问修饰符无额外限制
目标类(如 UserServiceImpl)在实现接口方法时,可以将方法声明为 public(推荐且必须)、protected 或包级(不加修饰符),但:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 如果实现方法不是
public,虽然编译可通过,但运行时代理对象调用该方法时会抛出IllegalAccessException(因为代理类通过反射调用,而反射对非public成员需显式设置setAccessible(true),但 JDK 代理内部未做此处理); - Spring 默认严格遵循规范,要求实现类中对接口方法的重写也必须是
public,否则可能触发代理失败或行为异常。
不能代理的几种情况
以下情形会导致 JDK 动态代理无法生效或抛异常:
- 接口中定义了
default方法但目标类未重写,且你试图对该default方法织入切面——Spring 仍能代理,但增强逻辑是否执行取决于代理配置和 JDK 版本兼容性; - 目标类有
public但未在接口中声明的方法(比如sendEmail()),JDK 代理完全无法拦截,因为代理对象类型只认接口契约; - 方法签名在接口中存在,但目标类实现时用了
final或private修饰——这本身违反接口实现规则,编译不通过。
Spring 中的实际表现
Spring AOP 默认启用 JDK 动态代理(@EnableAspectJAutoProxy(proxyTargetClass = false)),它会:
- 检查目标 bean 是否至少实现一个接口;
- 仅对这些接口中声明的
public方法创建代理; - 忽略目标类中任何非接口方法,无论其访问修饰符如何。
如果你需要代理没有接口的类,或想拦截 package-private / protected 方法,就得切换到 CGLIB 代理(设 proxyTargetClass = true),但要注意 CGLIB 无法代理 final 类或 final 方法。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










