解決@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ì)話(huà),需要通過(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í)戰(zhàn)應(yīng)用
本章全面介紹了泛型的核心概念、設(shè)計(jì)初衷及三大使用方式(泛型類(lèi)、泛型接口、泛型方法),重點(diǎn)講解了泛型的擦除機(jī)制和通配符的靈活運(yùn)用,強(qiáng)調(diào)了泛型在編譯期類(lèi)型安全和運(yùn)行時(shí)類(lèi)型擦除的優(yōu)勢(shì),感興趣的朋友跟隨小編一起看看吧2026-03-03
SpringCloud與Consul集成實(shí)現(xiàn)負(fù)載均衡功能
負(fù)載均衡基本概念有:實(shí)服務(wù)、實(shí)服務(wù)組、虛服務(wù)、調(diào)度算法、持續(xù)性等,其常用應(yīng)用場(chǎng)景主要是服務(wù)器負(fù)載均衡,鏈路負(fù)載均衡。這篇文章主要介紹了SpringCloud與Consul集成實(shí)現(xiàn)負(fù)載均衡 ,需要的朋友可以參考下2018-09-09
Spring?Cloud?Ribbon?負(fù)載均衡使用策略示例詳解
Spring?Cloud?Ribbon?是基于Netflix?Ribbon?實(shí)現(xiàn)的一套客戶(hù)端負(fù)載均衡工具,Ribbon客戶(hù)端組件提供了一系列的完善的配置,如超時(shí),重試等,這篇文章主要介紹了Spring?Cloud?Ribbon?負(fù)載均衡使用策略示例詳解,需要的朋友可以參考下2023-03-03
解決在微服務(wù)環(huán)境下遠(yuǎn)程調(diào)用feign和異步線程存在請(qǐng)求數(shù)據(jù)丟失問(wèn)題
這篇文章主要介紹了解決在微服務(wù)環(huán)境下遠(yuǎn)程調(diào)用feign和異步線程存在請(qǐng)求數(shù)據(jù)丟失問(wèn)題,主要包括無(wú)異步線程得情況下feign遠(yuǎn)程調(diào)用,異步情況下丟失上下文問(wèn)題,需要的朋友可以參考下2022-05-05

