一、引言
小編最近接了個成功的服務端項目,在他們項目中集成了一個WebSocket,嗯,其實不難,有很多成功案例。
本文主要來記錄一下,如果沒有百度在自己的項目啟動就拋開一些奇怪的問題,或者有一些奇奇怪怪的問題,靠自己怎么排查問題,另一種引擎去清除所有的問題都能夠通過搜索來解決。
除了力,那我們就只剩下:“異常信息、來源、和自己的經驗” 。
項目框架用的是SpringBoot,然后集成WebSocket就行,在集成的過程中,其中有一步需要配置一個Bean。
@Component public class WsConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { ServerEndpointExporter serverEndpointExporter = new ServerEndpointExporter(); return serverEndpointExporter; } }
然后在項目啟動的時候拋出了異常信息:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverEndpointExporter' defined in class path resource [org/springblade/modules/hol/config/WsConfig.class]: Invocation of init method failed; nested exception is JAVA.lang.IllegalStateException: javax.websocket.server.ServerContainer not available at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1769) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:514) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:321) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:319) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:863) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:744) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:391) at org.springframework.boot.SpringApplication.run(SpringApplication.java:312) at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:140) at org.springblade.core.launch.BladeApplication.run(BladeApplication.java:50) at org.springblade.Application.main(Application.java:18) Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available at org.springframework.util.Assert.state(Assert.java:73) at org.springframework.web.socket.server.standard.ServerEndpointExporter.afterPropertiesSet(ServerEndpointExporter.java:107) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1828) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1765) ... 16 common frames omitted
基于 Spring Boot + MyBatis Plus + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
- 項目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro
- 視頻教程:https://doc.iocoder.cn/video/
二、問題分析
?
在類路徑資源 [org/springblade/modules/hol/config/WsConfig.class] 中定義名稱為“serverEndpointExporter”的 bean 創建錯誤:調用 init 方法失敗;嵌套異常是 java.lang.IllegalStateException: javax.websocket.server.ServerContainer 不可用
?
通過異常的第一行就可以得知,是在 Spring 創建“serverEndpointExporter”beanW 的時候拋出來的,是這個配置類,原因是:ServerContainer 這個對象不可使用。
拋出異常的類是:AbstractAutowireCapableBeanFactory.java:1769
分析這里就可以得知,是在spring創建serverEndpoint的時候異常了, “那么異常的原因是Exporter ,為什么ServerContainer不可用呢?” 接下來分析
通過點擊轉換的源碼信息:AbstractAutowireCapableBeanFactory.java:1769
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( // 1769 這里就是1769行~~ (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
通過查看源碼,是在1796行的catch里面拋出的異常的,catch是捕捉到invokeInitMethods這個異常的方法,然后才拋出來的,這樣就可以開始調試了。
我們是在創建serverendpointExporter的,加一個篩選條件beanName為serverEndpointExporter,這樣再過濾創建異常的其他bean。
斷點卡好,下調試模式來啟動項目,然后F5進到vokeInitMethods,單步往下執行。
最后執行到了 ((InitializingBean) bean).afterPropertiesSet(); 這行代碼,bean方法對象是ServerEndpointExporter,調用了這個對象的afterPropertiesSet。
這個ServerEndpointExporterjava類,其中就有在獲取serverPropertiesSet方法之后,首先調用這個getServerContainer方法獲取serverContainer對象。
很明顯這里返回的肯定是null,null != null 結果肯定是false,最后就是這樣調用state(false,javax.websocket.server.ServerContainer not available)
通過state源知道,最后拋出這個異常:java.lang.IllegalStateContainerException: javax.websocket.server.server not available
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的后臺管理系統 + 用戶小程序,支持 RBAC 動態權限、多租戶、數據權限、工作流、三方登錄、支付、短信、商城等功能
- 項目地址:https://gitee.com/zhijiantianya/yudao-cloud
- 視頻教程:https://doc.iocoder.cn/video/
三、解決問題
通過這個文章上面的分析可以確認是因為ServerEndpoint類有一個對象為空的ExserverContainer,所以它會為空。
就需要,服務器容器這個對象是在哪里的,找源頭就只有兩個地方了,這就好辦了。
public class ServerEndpointExporter extends WebApplicationObjectSupport implements InitializingBean, SmartInitializingSingleton { @Nullable private List<Class<?>> annotatedEndpointClasses; @Nullable private ServerContainer serverContainer; // 有一個Set方法可以為serverContainer賦值 public void setServerContainer(@Nullable ServerContainer serverContainer) { this.serverContainer = serverContainer; } @Nullable protected ServerContainer getServerContainer() { return this.serverContainer; } // 有一個init方法可以為serverContainer賦值 @Override protected void initServletContext(ServletContext servletContext) { if (this.serverContainer == null) { this.serverContainer = (ServerContainer) servletContext.getAttribute("javax.websocket.server.ServerContainer"); } } @Override public void afterPropertiesSet() { Assert.state(getServerContainer() != null, "javax.websocket.server.ServerContainer not available"); } }
通過IDEA的快捷方式,發現沒有其他地方調用setServerContainer方法,那么只有init方法了,那么就斷點卡在initServletContext方法了。
通過Bebug的能力發現,在從servletContext.getAttribute,根本就沒有返回javax.websocket.ServerContainer,就是一個null的時候,結果就產生了異常給serverContainer,所以就產生了異常。
「那為什么javax.websocket.server.ServerContainer會不存在呢?」
這個類,原來是一個接口,那個ServerContainer肯定有的實現類,通過IDEA查看好人,到這個小編大概就明白了這個項目的其他jar包,引發了沖突。
最后到是哪個罐子最后突破了,直接通過IDEA功能就能夠定位到,通過maven helper工具定位到maven引用位置,然后移除掉。
一下maven,項目就正常啟動啦~~~~~~
四、劃重點
本文針對某個異常、或者某個具體問題的另外來進行分析,分享解決這個異常的一個過程。
最開始的小井剛畢業那會,遇到個個,運氣就過好,人都踩到這個了,解決這個問題,一時也找不到很多合適的文章,然后能力,然后話就坑了咋知道辦。
肯定沒有其他人通過自己的問題來解決問題,而且通過自己發現問題的過程來解決自己的問題。
也有很多小伙伴問過小編,學習一些框架源碼有什么用?會用不就行了嗎?
怎么辦呢?真的會說到底怎么辦呢,那么你的話,這些奇怪的東西是怎么實現的?深層次的理解。
久而久之之,遇到主動問題,你去摸索的第一個反應自己去百度,會直接為啥子看看的異常情況,分析一下不明的源頭,再去學習,進行進行。
就這樣不知不覺你的技術會慢慢升華,那個時候感覺就特別明顯了。