Spring Boot自注入中私有方法可调用及Autowired对象为空原因解析
关于Spring Boot中CGLIB代理私有方法的两个问题解答
一、为何私有方法可被正常调用?
CGLIB基于继承生成代理类,子类确实无法重写父类的私有方法,但Java的方法调用规则允许:当通过子类实例(代理对象)触发父类私有方法调用时,由于该方法无法被代理类覆盖,调用会直接执行父类中原本的私有方法逻辑。
需要注意的是,私有方法本身只能在原类内部直接调用,实际场景中你应该是在TestService的某个方法里通过this调用privateTest()——当这个方法被代理对象触发时,this指向代理对象,但调用私有方法时会直接走父类的方法实现,因此不会抛出访问异常。
二、为何Spring Boot中调用私有方法时Autowired对象为空?
这和Spring的CGLIB代理创建逻辑直接相关:
- Spring创建代理时,先生成目标对象(也就是
TestService的真实实例),这个对象已经完成构造注入,otherBean和自注入的testService都已赋值。 - 随后生成的CGLIB代理类实例,并不会通过构造器初始化父类(
TestService)的属性,而是通过方法拦截器(MethodInterceptor)处理所有可重写的方法(public/protected)。 - 当调用代理对象的
publicTest()方法时,拦截器会将调用转发给目标对象的publicTest(),此时方法内的this是目标对象,所以otherBean不为空。 - 但如果调用代理对象的
privateTest()方法(比如this指向代理对象时调用),由于私有方法无法被拦截,会直接执行代理对象自身的父类方法逻辑——而代理对象的父类属性(otherBean)从未被初始化(代理对象不是通过构造注入创建的,没有执行父类的构造器赋值),因此otherBean为空,此时断点显示的this就是代理类实例。
对比你的纯Java测试代码:手动new ProxyClass时明确调用了父类构造器super(value),初始化了父类的value属性,所以print()能输出正确值;但Spring的CGLIB代理对象并非通过常规构造流程创建,自然不会初始化父类的注入属性。
纯Java测试代码
public class AClass { private String value; public AClass(String value) { this.value = value; } public static void main(String[] args) { AClass a = new ProxyClass("test"); a.print(); } private void print() { System.out.println(this.value); } } public class ProxyClass extends AClass { public ProxyClass(String value) { super(value); } }
Spring Boot测试代码
@Service @RequiredArgsConstructor public class TestService { private final TestService testService; private final Object otherBean; public void publicTest(){ // callee = TestService otherBean.doSomeThing(); } private void privateTest(){ // callee = TestService$$SpringCGLIB and all autowired are null otherBean.doSomeThing(); } }
内容的提问来源于stack exchange,提问作者just_code_dog
相关产品推荐
相关产品推荐

