Spring-17 依赖项空指针异常原因
Spring 源码系列文章会遵循由浅入深,由易到难,由宏观到微观的原则,目标是尽量降低学习难度,而不是一上来就迷失在源码当中. 文章会从一个场景作为出发点,针对性的目的性极强的针对该场景对 Spring 的实现原理,源码进行探究学习。该系列文章会让你收获什么? 从对 Spring 的使用者成为 Spring 专家。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。
使用 spring 的时候有时会遇到明明依赖项都注入进去了,但是在方法调用的时候却会报空指针异常,本章会说明这种现象产生的原因是什么。
直接说结论吧,少一点废话多一点有价值的内容。产生 NPE 的原因是因为调用了代理对象的 private
方法,而 private
方法中使用带了依赖项,这时候就会报空指针异常。
原因
在 spring 的 AOP
中由代理对象和目标对象共同组成,代理对象和目标对象是两个完全不同的对象. cglib
代理方式是代理类继承了目标类. 最终得到的代理类中的依赖项属性是 null , 因为代理类是目标类的子类, 也继承了目标类中的属性,但是却不会给这些属性再赋值。
debug 可以看到 targetSource
中的目标对象中 bean2
属性是有值的. 代理对象实列中 bean2
是 null 。
再强调一遍产生 NPE
的原因, 调用了代理对象中的 private
方法,在 private
方法使用了依赖注入项。 这种情况较多的出现在 controller 层,因为 SpringMVC
是通过反射调用目标方法的,所以即使申明为 private
也无所谓。但是如果 controller 是代理对象,这样就会出现 NPE
。
SpringMVC
对目标控制器方法的反射调用:
测试代码
public class NPEExp {
public static void main(String[] args) throws Exception {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger logger = loggerContext.getLogger(AnnotationAwareAspectJAutoProxyCreator.class);
logger.setLevel(Level.TRACE);
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
AnnotationAwareAspectJAutoProxyCreator proxyCreator = new AnnotationAwareAspectJAutoProxyCreator();
proxyCreator.setBeanFactory(factory);
factory.addBeanPostProcessor(proxyCreator);
AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor();
processor.setBeanFactory(factory);
factory.addBeanPostProcessor(processor);
factory.registerBeanDefinition("myAspect" , BeanDefinitionBuilder
.genericBeanDefinition(MyAspect.class)
.getBeanDefinition());
factory.registerBeanDefinition("bean1" , BeanDefinitionBuilder
.genericBeanDefinition(Bean1.class)
.getBeanDefinition());
factory.registerBeanDefinition("bean2" , BeanDefinitionBuilder
.genericBeanDefinition(Bean2.class)
.getBeanDefinition());
Bean1 bean1 = factory.getBean(Bean1.class);
bean1.foo();
bean1.fooPrivate();
Method method = Bean1.class.getDeclaredMethod("fooPrivate");
method.setAccessible(true);
method.invoke(bean1);
}
@Aspect
static class MyAspect {
@Before("execution(* foo())")
public void before() {
System.out.println("@Aspect @Before ------------------------------");
}
}
static class Bean1 {
@Autowired
private Bean2 bean2;
public Bean1() {
System.out.println("======================== bean1 实例化 ");
}
public void foo() {
System.out.println("======================== Bean1 foo ");
bean2.foo();
}
private void fooPrivate() {
System.out.println("======================== Bean1 fooPrivate ");
bean2.foo();
}
}
static class Bean2 {
public Bean2() {
System.out.println("======================== Bean2 实例化 ");
}
public void foo() {
System.out.println("======================== Bean2 foo ");
}
}
}
DevX
会持续分享 Java
技术干货,如果你觉得本文对你有帮助希望你可以分享给更多的朋友看到。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。