springboot反射执行private方法@Autowired字段为空
- 描述
 - 错误复现
 - controller
 - service
 - ReflectServiceImpl
 - ReflectCallService
 - service 层切面
 
- debug 结果图
 - 调用 reflectTest 方法(public反射)
 - 调用 reflectTest1方法(private反射)
 
- 分析
 - 参考
 
描述
业务代码写完之后,懒得写
mock代码,想直接暴露个接口测一下。但是因为目标方法是个private方法,所以不能直接用@Autowired注入类实例然后调用方法,便想到用反射来调用方法。然后,不试不知道,一试吓一跳。代码看着没问题,但是执行的时候报空指针异常(私有方法中有调用注入的其他service方法,这个service是空)
错误复现
controller
package com.yichen.casetest.controller;
// ... 其他import
@Controller
@RequestMapping("/test")
@Slf4j
public class TestController {
	@Autowired
    private ReflectServiceImpl reflectService;
    @PostMapping("/reflectTest")
    @ResponseBody
    public Object reflectTest(@RequestParam String name, @RequestParam String age){
        try {
            Method method = ReflectServiceImpl.class.getDeclaredMethod("reflectTest", String.class, String.class);
            return method.invoke(reflectService, name, age);
        }
        catch (Exception e){
            log.error("reflectTest出现错误{}", e.getMessage(), e);
        }
        return "error";
    }
    @PostMapping("/reflectTest1")
    @ResponseBody
    public Object reflectTest1(@RequestParam String name, @RequestParam String age){
        try {
            Method method = ReflectServiceImpl.class.getDeclaredMethod("reflectTest1", String.class, String.class);
            method.setAccessible(true);
            return method.invoke(reflectService, name, age);
        }
        catch (Exception e){
            log.error("reflectTest出现错误{}", e.getMessage(), e);
        }
        return "error";
    }
}
 
service
ReflectServiceImpl
package com.yichen.casetest.test.service.reflect.impl;
import com.yichen.casetest.test.service.reflect.ReflectCallService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class ReflectServiceImpl extends AbstractReflectService {
    @Autowired
    private ReflectCallService reflectCallService;
    public String reflectTest(String name, String age){
        String s = name + "-" + age;
        log.info("==> {} ==> ???", s);
        return reflectCallService.getName();
    }
    private String reflectTest1(String name, String age){
        String s = name + "-" + age;
        log.info("==> {} ==> ???", s);
        return reflectCallService.getName();
    }
    @Override
    public String getCombineData(String name, String age){
        String s = name + "-" + age;
        log.info("==> {}", s);
        return reflectCallService.getName();
    }
    @Override
    public String addressFrom() {
        rainNow();
        return reflectCallService.getName();
    }
}
 
ReflectCallService
package com.yichen.casetest.test.service.reflect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ReflectCallService {
    @Autowired
    private TransactionService transactionService;
    public String getName(){
        transactionService.save();
        return "shanliang";
    }
}
 
service 层切面
package com.yichen.casetest.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class LogAspect {
    @Pointcut("execution(* com.yichen.casetest.test.service..*.*(..))")
//    @Pointcut("execution(* com.yichen.casetest.test.service.reflect.impl.ReflectServiceImpl.*(..))")
    public void logAspect() {
    }
    @Before("logAspect()")
    public void before(JoinPoint joinPoint){
        log.info("{} logAspect before", joinPoint.getTarget().getClass().getName());
    }
    @After("logAspect()")
    public void after(JoinPoint joinPoint){
        log.info(" {} logAspect after", joinPoint.getTarget().getClass().getName());
    }
}
 
debug 结果图
调用 reflectTest 方法(public反射)

可以看到,
AutoWired字段是有值的,而是是一个cglib代理。
调用 reflectTest1方法(private反射)

这里可以看到,
AutoWired字段是个null
分析
如果把
LogAspect注销,则private和public都可以通过反射执行。那么问题就是出在cglib代理身上了。具体看一下cglib代理的描述:
动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
可以看到它是通过生成子类的方法来创建代理。而
java中子类是不继承父类的private方法的。这一点就是导致上面问题的原因。具体点说,因为启用了cglib代理,所以类的属性都通过代理绑定了,实际属性字段是null
如果是非private,以及非final修饰的方法,都会通过代理最终调用实际对象,而实际对象的@autowired字段是有值的。但如果是private、final修饰的方法,则会直接用属性,而此时属性为null,因为被代理了。。
参考
Spring AOP中private(踩坑)实践总结


![[附源码]计算机毕业设计JAVA汽车租赁系统](https://img-blog.csdnimg.cn/6f7a202f91964d7382b633a5304333f0.png)

















