前言
曾经在简书上看过一篇博客说写博客时加个前言纯粹是在说废话,其实我觉得我现在确实在说废话,但是前言有一个作用可以让博主描绘此刻的心情,比如我现在解决了这个问题后内心千万只草泥马在奔腾,所以需要写个前言平复一下心情好缕清自己的思路
工具类
首先我写了一个工具类继承ApplicationContextAware接口方便个别无法使用@Autowired注入的类注入spring容器中
工具类代码如下:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author ZP
* @date 2020/4/22.
*/
@Component
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
// 获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
// 通过name获取 Bean.
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
// 通过class获取Bean.
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
// 通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
}
其实在我写的demo里还是可以正常注入的,但是集成进系统后报错,最怕碰到这种迷惑bug,这边可以,那边不可以。最后通过两边的同时debug猜出问题所在。
比如我的报错在第27行
Caused by: java.lang.NullPointerException: null
at com.zp.websocket.SpringUtil.getBean(SpringUtil.java:27) ~[classes/:na]
at com.zp.websocket.MyWebSocket.<init>(MyWebSocket.java:22) ~[classes/:na]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_111]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_111]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_111]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_111]
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:172) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
... 19 common frames omitted
这个是在demo成功注入情况下的debug截图
这是系统失败注入情况下的debug截图
显而易见,失败的原因是类内部的applicationContext没有被实例,所以我们到实例applicationContext的代码上并加上断点康康
这是成功的时候
而失败的情况是哪怕打了断点但还没执行重写的setApplicationContext
方法就抛出异常了,而我这个工具类只在MyWebSocket.java里用到,他们的位置如图
是在同一个包下的,demo里的位置是
在两个不同的包下,那么会不会是因为位置问题,spring启动时的加载类顺序问题,在同一个包下从上而下扫描,所以是先MyWebSocket再SpringUtil。为此我还特地做个试验,把SpringUtil改名为MySpringUtil使其在上面
结果真的能执行到setApplicationContext并且没有抛异常
好吧,真的是顺序问题,spring原本先注入我的MyWebSocket,因为我的MyWebSocket里用到了继承ApplicationContextAware的SpringUtil,而此时我的SpringUtil还没注入spring容器里最后导致空指针异常。解决方法就是改一下顺序,让SpringUtil先执行,这样继承ApplicationContextAware的setApplicationContext方法就会先执行确保applicationContext有实例对象,这样就能避免异常了。