Spring Boot 無法自動裝配靜態(tài)成員變量以及new出的對象,導致運行時出現(xiàn)空指針異常的解決方法。
問題:
在項目中使用 WebSocket
便于雙向通信,在類 WebSocketServer
中進行了基礎(chǔ)的配置后爽丹,在其內(nèi)部封裝了一些發(fā)送信息的方法,因此我希望它需要被外界調(diào)用辛蚊。
WebSocketServer
中的部分代碼:
/**
* websocket信息處理
*/
@Slf4j
@Component
@ServerEndpoint(value = "/ws/{sid}") //將類定義成一個WebSocket服務(wù)器端
public class WebSocketServer {
//存放會話對象
private static Map<String, Session> sessionMap = new HashMap();
......
/**
* 群發(fā)消息
*
* @param message 發(fā)送的消息
*/
public void sendToAllClient(String message) {
Collection<Session> sessions = sessionMap.values();
for (Session session : sessions) {
//服務(wù)器向客戶端發(fā)送消息
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}s
}
......
}
因為此類已經(jīng)添加了@Component
注解交付給Spring容器管理粤蝎,所以我在我的工具類MyUtil
中通過@Autowired
注解自動注入,以便于使用袋马。
MyUtil
工具中的部分代碼:
@Component
public class MyUtil {
@Autowired //自動注入
private static WebSocketServer webSocketServer;
//使用服務(wù)
public static void useServer() {
//群發(fā)消息
webSocketServer.sendToAllClient("hello");
}
}
緊接著我們模擬使用場景初澎,在 SpringText
下進行使用測試÷橇荩看看會怎么樣
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class XXXServerApplicationTests {
@Test
public void autowiredText() {
MyUtil.useServer();
}
}
很好碑宴,很快完美的報錯,null指針異常桑谍。
原因:
這個原因是 Spring Boot 不會自動裝配靜態(tài)的成員(這個看上面的結(jié)果應該也能看出來延柠,因為靜態(tài)的成員一般是不允許實體對象所調(diào)用的,所以Spring所管理的bean對象無法通過對象.set的方法去自動注入bean)锣披。
其實在我后面尋找解決方案的時候發(fā)現(xiàn)贞间,Spring Boot 也不會自動裝配新new出來的對象(因為新new出來的對象都不是spring容器所管理的贿条,所以肯定也不能完成自動裝配,但是一般人也不會像我這樣new出來使用吧哈)增热。
解決方法:
既然 Spring Boot 不會自動裝配整以,那就只能通過最原始的方法,即通過獲取ApplicationContext
對象獲取spring 的 bean 對象钓葫。
具體實現(xiàn)方法是實現(xiàn)一個類悄蕾,該類實現(xiàn)ApplicationContextAware
接口,并且重寫其setApplicationContext()
方法础浮,以存儲spring容器對象(目的就是為了獲取ApplicationContext
并存儲進此類中)帆调。然后通過容器對象獲取spring的bean;
具體代碼如下:
package com.anyi.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
/**
* springContext 豆同,實現(xiàn)接口番刊,進行 springBean 的手動裝配
*/
@Component
public class ManagementSpringBeans implements ApplicationContextAware {
/**
* 創(chuàng)建 ApplicationContext 保存spring容器對象
*/
private static ApplicationContext context;
/**
* 通過class文件獲取對應的bean你
*
* @param requiredType 請求類的class文件
* @param <T> 請求bean的類
* @return 對應的bean
*/
public static <T> T getBean(Class<T> requiredType) {
//通過 類class 獲取對象
return context.getBean(requiredType);
}
/**
* 重寫方法獲取spring的上下文對象
*
* @param applicationContext spring容器
* @throws BeansException beans異常
*/
@Override
public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {
//存儲spring容器對象
ManagementSpringBeans.context = applicationContext;
}
}
實現(xiàn)完成這個 ManagementSpringBeans
后,以后要想注入bean的對象影锈,就可以調(diào)用其內(nèi)部的 getBean()
靜態(tài)方法芹务,傳參對應的類的class就可獲取對應的bean,手動裝配鸭廷。具體實現(xiàn)如下所示:
public class MyUtil {
private static WebSocketServer webSocketServer = ManagementSpringBeans.getBean(WebSocketServer.class);
public static void useServer() {
//群發(fā)消息
webSocketServer.sendToAllClient("hello");
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class XXXServerApplicationTests {
@Test
public void autowiredText() {
MyUtil.useServer();
}
}
成功 !
注意:使用這個方法當然很好岛都,因為幾乎可以在任何地方都能裝配bean了舷夺。但是這個方案太自由了,可能會造成代碼上的一些破壞。(就和原先的
goto
語句一樣)所以雖然這是一個解決方法但是也不是那么的好拔创。最后我還是使用原生的
springboot
自動裝配來解決問題奸笤。當你在開發(fā)時使用這個方法進行手動裝配的時候陶冷,可能是因為原始的設(shè)計布局有些問題儒溉。