解決@ServerEndpoint不能注入@Autowired的問題
都會(huì)報(bào)空指針錯(cuò)誤的原因
本質(zhì)原因
spring管理的都是單例(singleton)和 websocket (多對(duì)象)相沖突。
?因?yàn)閃ebSocket 是一個(gè)多例,因?yàn)槟阈枰鄠€(gè)對(duì)象來保存鏈接,所以就和單例無緣了,然而我們交給Spring管理的是單例的東西,那么我們可以從這里知道,WebSocket的對(duì)象是不能交給Spring管理的
此時(shí)我們有兩種解決方案;
方案1: 在上下文獲取你需要的對(duì)象
package cn.jiabao.util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Repository;
@Repository
public class SpringUtils implements BeanFactoryPostProcessor {
//Spring應(yīng)用上下文環(huán)境
private static ConfigurableListableBeanFactory beanFactory;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SpringUtils.beanFactory = beanFactory;
}
public static ConfigurableListableBeanFactory getBeanFactory() {
return beanFactory;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException {
return (T) getBeanFactory().getBean(name);
}
public static <T> T
getBean(Class<T> clz) throws BeansException {
T result = (T) getBeanFactory().getBean(clz);
return result;
}
}
--------------------------------------------------------
private StringRedisTemplate stringRedisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
@OnOpen
public void OnOpen(Session session, @PathParam(value = "name") String name,@PathParam(value = "password") String password) {
stringRedisTemplate.opsForValue().set("1","1");
this.session = session;
this.name = name;
// name是用來表示唯一客戶端,如果需要指定發(fā)送,需要指定發(fā)送通過name來區(qū)分
webSocketSet.put(name, this);
log.info("[WebSocket] 連接成功,當(dāng)前連接人數(shù)為:={}", webSocketSet.size());
//接收用戶傳入的參數(shù),并且進(jìn)行判斷登錄是否成功 如果登錄成功那 personService.savePersonSql(name,password);么 就保存Sesion 并且 鏈接成功 否職反之
// personService.savePersonSql(name,"1"); //多例之后 可以執(zhí)行reids代碼等等
// 注意: 不能在這里面直接用Reids 即使多例 直接保存也有問題
}使用上面的工具類就,可以從上下文獲取到你需要的實(shí)例對(duì)象
方案2: 既然不給那我就自己搞一個(gè)注入進(jìn)去
private static StringRedisTemplate stringRedisTemplate;
@Autowired
private void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate){
Link.stringRedisTemplate =stringRedisTemplate;
}心得
? 這里L(fēng)ink的多對(duì)象機(jī)制和Spring 的controller注解截止可以同時(shí)進(jìn)行,互相沒有矛盾。
Spring在初始的時(shí)候,創(chuàng)建一個(gè)Link的對(duì)象,該對(duì)象在運(yùn)行期間不會(huì)被使用,同時(shí)注入的類進(jìn)行了靜態(tài)的完善,這是Spring在這套代碼里面的唯一作用,當(dāng)有用戶聊天的時(shí)候,java會(huì)根據(jù)Link類創(chuàng)建對(duì)象,每個(gè)對(duì)象保持對(duì)應(yīng)的用戶鏈接,因?yàn)殪o態(tài)類已經(jīng)在啟動(dòng)的時(shí)候被Spring初始化了,所以此時(shí)每個(gè)對(duì)象都可以被正常訪問
注意:
@Component 這個(gè)注解需要有,不然就不能被加載到Spring 就不能在啟動(dòng)服務(wù)器的時(shí)候進(jìn)行正常的鏈接
package cn.jiabao.link;
import cn.jiabao.util.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@ServerEndpoint("/websocket/{name}/{password}")
@Component
@Controller
public class Link {
private static StringRedisTemplate stringRedisTemplate;
@Autowired
private void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate){
Link.stringRedisTemplate =stringRedisTemplate;
}
/**
* 與某個(gè)客戶端的連接對(duì)話,需要通過它來給客戶端發(fā)送消息
*/
private Session session;
/**
* 標(biāo)識(shí)當(dāng)前連接客戶端的用戶名
*/
private String name;
/**
* 用于存所有的連接服務(wù)的客戶端,這個(gè)對(duì)象存儲(chǔ)是安全的
*/
private static ConcurrentHashMap<String, Link> webSocketSet = new ConcurrentHashMap<>();
// private StringRedisTemplate stringRedisTemplate = SpringUtils.getBean(StringRedisTemplate.class);
@OnOpen
public void OnOpen(Session session, @PathParam(value = "name") String name,@PathParam(value = "password") String password) {
stringRedisTemplate.opsForValue().set("1","1");
this.session = session;
this.name = name;
// name是用來表示唯一客戶端,如果需要指定發(fā)送,需要指定發(fā)送通過name來區(qū)分
webSocketSet.put(name, this);
log.info("[WebSocket] 連接成功,當(dāng)前連接人數(shù)為:={}", webSocketSet.size());
//接收用戶傳入的參數(shù),并且進(jìn)行判斷登錄是否成功 如果登錄成功那 personService.savePersonSql(name,password);么 就保存Sesion 并且 鏈接成功 否職反之
// personService.savePersonSql(name,"1"); //多例之后 可以執(zhí)行reids代碼等等
// 注意: 不能在這里面直接用Reids 即使多例 直接保存也有問題
}
@OnMessage
public void OnMessage(String message) {
log.info("[WebSocket] 收到消息:{}", message);
//判斷是否需要指定發(fā)送,具體規(guī)則自定義
if (message.indexOf("TOUSER") == 0) {
String name = message.substring(message.indexOf("TOUSER") + 6, message.indexOf(";"));
AppointSending(name, message.substring(message.indexOf(";") + 1, message.length()));
} else {
GroupSending(message);
}
}
/**
* 群發(fā)
*
* @param message
*/
public void GroupSending(String message) {
for (String name : webSocketSet.keySet()) {
try {
webSocketSet.get(name).session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 指定發(fā)送
*
* @param name
* @param message
*/
public void AppointSending(String name, String message) {
try {
webSocketSet.get(name).session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Link getPerson(String key) {
return webSocketSet.get(key);
}
public static ConcurrentHashMap<String, cn.jiabao.link.Link> getMap() {
return webSocketSet;
}
}總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java線程活鎖的實(shí)現(xiàn)與死鎖等的區(qū)別
活鎖是一種遞歸情況,其中兩個(gè)或更多線程將繼續(xù)重復(fù)特定的代碼邏輯,本文主要介紹了Java線程活鎖的實(shí)現(xiàn)與死鎖等的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下2024-04-04
Java讀取json數(shù)據(jù)并存入數(shù)據(jù)庫的操作代碼
很多朋友問大佬們JAVA怎么把json存入數(shù)據(jù)庫啊,這一問題就把我難倒了,糾結(jié)如何操作呢,下面小編把我的經(jīng)驗(yàn)分享給大家,感興趣的朋友一起看看吧2021-08-08
SpringBoot調(diào)用DeepSeek接口的實(shí)現(xiàn)
本文主要介紹了SpringBoot調(diào)用DeepSeek接口的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02
SpringBoot動(dòng)態(tài)定時(shí)功能實(shí)現(xiàn)方案詳解
在SpringBoot項(xiàng)目中簡單使用定時(shí)任務(wù),不過由于要借助cron表達(dá)式且都提前定義好放在配置文件里,不能在項(xiàng)目運(yùn)行中動(dòng)態(tài)修改任務(wù)執(zhí)行時(shí)間,實(shí)在不太靈活?,F(xiàn)在我們就來實(shí)現(xiàn)可以動(dòng)態(tài)修改cron表達(dá)式的定時(shí)任務(wù),感興趣的可以了解一下2022-11-11
使用eclipse創(chuàng)建java項(xiàng)目的方法

