解決@ServerEndpoint不能注入@Autowired的問(wèn)題
都會(huì)報(bào)空指針錯(cuò)誤的原因
本質(zhì)原因
spring管理的都是單例(singleton)和 websocket (多對(duì)象)相沖突。
?因?yàn)閃ebSocket 是一個(gè)多例,因?yàn)槟阈枰鄠€(gè)對(duì)象來(lái)保存鏈接,所以就和單例無(wú)緣了,然而我們交給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是用來(lái)表示唯一客戶(hù)端,如果需要指定發(fā)送,需要指定發(fā)送通過(guò)name來(lái)區(qū)分 webSocketSet.put(name, this); log.info("[WebSocket] 連接成功,當(dāng)前連接人數(shù)為:={}", webSocketSet.size()); //接收用戶(hù)傳入的參數(shù),并且進(jìn)行判斷登錄是否成功 如果登錄成功那 personService.savePersonSql(name,password);么 就保存Sesion 并且 鏈接成功 否職反之 // personService.savePersonSql(name,"1"); //多例之后 可以執(zhí)行reids代碼等等 // 注意: 不能在這里面直接用Reids 即使多例 直接保存也有問(wèn)題 }
使用上面的工具類(lèi)就,可以從上下文獲取到你需要的實(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)行,互相沒(méi)有矛盾。
Spring在初始的時(shí)候,創(chuàng)建一個(gè)Link的對(duì)象,該對(duì)象在運(yùn)行期間不會(huì)被使用,同時(shí)注入的類(lèi)進(jìn)行了靜態(tài)的完善,這是Spring在這套代碼里面的唯一作用,當(dāng)有用戶(hù)聊天的時(shí)候,java會(huì)根據(jù)Link類(lèi)創(chuàng)建對(duì)象,每個(gè)對(duì)象保持對(duì)應(yīng)的用戶(hù)鏈接,因?yàn)殪o態(tài)類(lèi)已經(jīng)在啟動(dòng)的時(shí)候被Spring初始化了,所以此時(shí)每個(gè)對(duì)象都可以被正常訪問(wèn)
注意:
@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è)客戶(hù)端的連接對(duì)話,需要通過(guò)它來(lái)給客戶(hù)端發(fā)送消息 */ private Session session; /** * 標(biāo)識(shí)當(dāng)前連接客戶(hù)端的用戶(hù)名 */ private String name; /** * 用于存所有的連接服務(wù)的客戶(hù)端,這個(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是用來(lái)表示唯一客戶(hù)端,如果需要指定發(fā)送,需要指定發(fā)送通過(guò)name來(lái)區(qū)分 webSocketSet.put(name, this); log.info("[WebSocket] 連接成功,當(dāng)前連接人數(shù)為:={}", webSocketSet.size()); //接收用戶(hù)傳入的參數(shù),并且進(jìn)行判斷登錄是否成功 如果登錄成功那 personService.savePersonSql(name,password);么 就保存Sesion 并且 鏈接成功 否職反之 // personService.savePersonSql(name,"1"); //多例之后 可以執(zhí)行reids代碼等等 // 注意: 不能在這里面直接用Reids 即使多例 直接保存也有問(wèn)題 } @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-04Java讀取json數(shù)據(jù)并存入數(shù)據(jù)庫(kù)的操作代碼
很多朋友問(wèn)大佬們JAVA怎么把json存入數(shù)據(jù)庫(kù)啊,這一問(wèn)題就把我難倒了,糾結(jié)如何操作呢,下面小編把我的經(jīng)驗(yàn)分享給大家,感興趣的朋友一起看看吧2021-08-08SpringBoot調(diào)用DeepSeek接口的實(shí)現(xiàn)
本文主要介紹了SpringBoot調(diào)用DeepSeek接口的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02通過(guò)Java帶你了解網(wǎng)絡(luò)IO模型
這篇文章將通過(guò)Java帶大家了解網(wǎng)絡(luò)IO模型,包括BIO,NoBlockingIO,NIO(NewIO),AIO等做了詳細(xì)得介紹,感興趣的小伙伴可以參考閱讀本文2023-05-05SpringBoot動(dòng)態(tài)定時(shí)功能實(shí)現(xiàn)方案詳解
在SpringBoot項(xiàng)目中簡(jiǎn)單使用定時(shí)任務(wù),不過(guò)由于要借助cron表達(dá)式且都提前定義好放在配置文件里,不能在項(xiàng)目運(yùn)行中動(dòng)態(tài)修改任務(wù)執(zhí)行時(shí)間,實(shí)在不太靈活?,F(xiàn)在我們就來(lái)實(shí)現(xiàn)可以動(dòng)態(tài)修改cron表達(dá)式的定時(shí)任務(wù),感興趣的可以了解一下2022-11-11

使用eclipse創(chuàng)建java項(xiàng)目的方法