解決@ServerEndpoint不能注入@Autowired的問題
都會報空指針錯誤的原因
本質原因
spring管理的都是單例(singleton)和 websocket (多對象)相沖突。
?因為WebSocket 是一個多例,因為你需要多個對象來保存鏈接,所以就和單例無緣了,然而我們交給Spring管理的是單例的東西,那么我們可以從這里知道,WebSocket的對象是不能交給Spring管理的
此時我們有兩種解決方案;
方案1: 在上下文獲取你需要的對象
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應用上下文環(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] 連接成功,當前連接人數為:={}", webSocketSet.size()); //接收用戶傳入的參數,并且進行判斷登錄是否成功 如果登錄成功那 personService.savePersonSql(name,password);么 就保存Sesion 并且 鏈接成功 否職反之 // personService.savePersonSql(name,"1"); //多例之后 可以執(zhí)行reids代碼等等 // 注意: 不能在這里面直接用Reids 即使多例 直接保存也有問題 }
使用上面的工具類就,可以從上下文獲取到你需要的實例對象
方案2: 既然不給那我就自己搞一個注入進去
private static StringRedisTemplate stringRedisTemplate; @Autowired private void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate){ Link.stringRedisTemplate =stringRedisTemplate; }
心得
? 這里Link的多對象機制和Spring 的controller注解截止可以同時進行,互相沒有矛盾。
Spring在初始的時候,創(chuàng)建一個Link的對象,該對象在運行期間不會被使用,同時注入的類進行了靜態(tài)的完善,這是Spring在這套代碼里面的唯一作用,當有用戶聊天的時候,java會根據Link類創(chuàng)建對象,每個對象保持對應的用戶鏈接,因為靜態(tài)類已經在啟動的時候被Spring初始化了,所以此時每個對象都可以被正常訪問
注意:
@Component 這個注解需要有,不然就不能被加載到Spring 就不能在啟動服務器的時候進行正常的鏈接
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; } /** * 與某個客戶端的連接對話,需要通過它來給客戶端發(fā)送消息 */ private Session session; /** * 標識當前連接客戶端的用戶名 */ private String name; /** * 用于存所有的連接服務的客戶端,這個對象存儲是安全的 */ 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] 連接成功,當前連接人數為:={}", webSocketSet.size()); //接收用戶傳入的參數,并且進行判斷登錄是否成功 如果登錄成功那 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; } }
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。