WebSocket结合hibernate+spring+json 实际经验

      最近一段时间因为需要,要做一个网页游戏。在某一段时间需要页面实时刷新。目前做网站普遍的思路都是js轮询的方式。由于是创新式的小项目,同组的好友提议了html5中提到的WebSocket,首先进行了技术调研。目前java方面支持的WebSocket的不算多。网上能搜到关于websocket实现框架的非常少。java EE7中加入WebSocket还没有看到。所以以来的包都来自于tomcat的支持。至少需要3个包tomcat-coyote,tomcat-catalina,tomcat-annotations-api,因为tomcat从7.027版本才开始比较好的支持websocket,在tomcat7.027之前的版本中,已经能使用websocket,但是会出现各式各样的问题。比如websocket连接后静置几秒钟就断开连接了等等。所以比较好的选择是使用7.027以上的版本。这3个jar包在相应tomcat的lib文件夹下都有。自从研究生期间接触maven后,慢慢了解到maven的强大,在这里不得不感叹一下。因为是个小型的敏捷团队,版本控制是必须的。在jar包控制这方面还是想通过maven来控制。就直接去maven中心库搜了搜。果真还是有的。小组讨论之后决定使用tomcat7.039(貌似40已经出了),到此解决了版本控制和jar包配置问题。pom关于tomcat3个jar包如下:

 1         <dependency>
 2             <groupId>org.apache.tomcat</groupId>
 3             <artifactId>tomcat-coyote</artifactId>
 4             <version>7.0.39</version>
 5         </dependency>
 6         <dependency>
 7              <groupId>org.apache.tomcat</groupId>
 8              <artifactId>tomcat-catalina</artifactId>
 9               <version>7.0.39</version>
10         </dependency>
11         <dependency>
12               <groupId>org.apache.tomcat</groupId>
13              <artifactId>tomcat-annotations-api</artifactId>
14              <version>7.0.39</version>
15         </dependency>

接下来是解决架构问题。现在在网上能搜到关于websocket的实践很少,基本能搜到的都是websocket架构和非常简单的例子(tomcat自带就有websocket例子),怎么样能将websocket机制运用起来。首先基本框架准备使用hibernate+spring mvc结合websocket,但是在实际试验中spring mvc和websocket会有部分有冲突。因为到前一段时间Spring Framework 4.0发布的版本中才有了JDK 8的支持和WebSocket编程的支持。所以现阶段需要别的方式来实现spring mvc+websocket。简单的解决方案就是写一个工具类来手动获得bean。解决spring和websocket支持之后需要解决的websocket的交互方式。websocket最直接的两个方法就是onTextMessage和onBinaryMessage,也就是字节流传输和字符流传输。最优方式便是设计一套自己传输协议。通过字节流传输。前后台分别解析协议获取交互操作。其次便可在onTextMessage也就是字符流上做文章。引入json便可以很好支持了。

配置websocket的步骤:

1实现一个类继承ContextLoaderListener,并且在web.xml中配置

 1 import javax.servlet.ServletContext;
 2 import javax.servlet.ServletContextEvent;
 3 
 4 import org.springframework.context.ApplicationContext;
 5 import org.springframework.web.context.ContextLoaderListener;
 6 import org.springframework.web.context.support.WebApplicationContextUtils;
 7 
 8 public class SpringLoaderListener extends ContextLoaderListener{
 9     
10     @Override
11     public void contextInitialized(ServletContextEvent event) {
12         super.contextInitialized(event);
13         ServletContext context=event.getServletContext();
14         ApplicationContext ctx=WebApplicationContextUtils.getRequiredWebApplicationContext(context);
15         SpringContextutil.setContext(ctx);
16         
17     }
18 
19 }

web.xml

1   <listener>
2         <listener-class>
3             XXXXXX.utils.SpringLoaderListener
4         </listener-class>
5 </listener>

获得spring bean工具类:

 1 import org.springframework.beans.BeansException;
 2 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 3 import org.springframework.context.ApplicationContext;
 4 
 5 public class SpringContextUtil {
 6     private static ApplicationContext context;
 7 
 8     public static ApplicationContext getContext() {
 9         return context;
10     }
11 
12     public static void setContext(ApplicationContext context) {
13         SpringContextutil.context = context;
14     }
15     
16     public static Object getBean(String name)throws BeansException{
17         return context.getBean(name);
18     }
19 
20     @SuppressWarnings("unchecked")
21     public static Object getBean(String name, Class requiredType) throws BeansException {   
22         return context.getBean(name, requiredType);   
23     }   
24   
25     public static boolean containsBean(String name) {   
26         return context.containsBean(name);   
27     }   
28   
29     public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {   
30         return context.isSingleton(name);   
31     }   
32   
33     @SuppressWarnings("unchecked")
34     public static Class getType(String name) throws NoSuchBeanDefinitionException {   
35         return context.getType(name);   
36     }   
37   
38     public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {   
39         return context.getAliases(name);   
40     }   
41 
42     
43 }

引入json所需jar包:

 1     <dependency>
 2      <groupId>com.google.code.gson</groupId>
 3       <artifactId>gson</artifactId>
 4       <version>2.2.3</version>
 5     </dependency>
 6     <dependency>
 7         <groupId>net.sf.json-lib</groupId>
 8         <artifactId>json-lib</artifactId>
 9         <version>2.4</version>
10     </dependency>
11     <dependency>
12      <groupId>net.sf.ezmorph</groupId>
13       <artifactId>ezmorph</artifactId>
14       <version>1.0.6</version>
15     </dependency>    

后台需要增加两个文件一个继承WebSocketServlet:

 1 import javax.servlet.annotation.WebServlet;
 2 import javax.servlet.http.HttpServletRequest;
 3 
 4 import org.apache.catalina.websocket.StreamInbound;
 5 import org.apache.catalina.websocket.WebSocketServlet;
 6 
 9 @WebServlet("/room")
10 public class RoomSocketServlet extends WebSocketServlet {
11     
12     private static final long serialVersionUID = -5853470534275847275L;
13     
14     @Override
15       protected StreamInbound createWebSocketInbound(String arg0,HttpServletRequest request) {
16         return  new RoomMessageInbound();
17       }
18       
19     }

一个继承MessageInbound:

 1 public class RoomMessageInbound extends MessageInbound{
 2     private static RoomModel room ;
 3     private UserModel user;
 4     private CommandDispatcherUtils commandDispatcher;
 5     
 6     public RoomMessageInbound() {
 7         if (commandDispatcher == null) {
 8             commandDispatcher = (CommandDispatcherUtils) SpringContextutil.getBean("commandDispatcher");
 9             room = RoomListModel.getInstance().getRoom(0);
10         }
11     }
12     
13     
14     @Override
15     protected void onOpen(WsOutbound outbound) 
16         room.addUser(outbound.hashCode());
17       super.onOpen(outbound);
18     }
19 
20     @Override
21     protected void onClose(int status) {
22       room.remove(getWsOutbound().hashCode());
23       super.onClose(status);
24     }
25 
26     @Override
27     protected void onBinaryMessage(ByteBuffer buffer) throws IOException {
28 
29     }
30 
31     @Override
32     protected void onTextMessage(CharBuffer buffer) throws IOException {
33       String msg = buffer.toString();
34       JSONObject report = JSONObject.fromObject(msg);
35       TemplateCommand command = commandDispatcher.getCommandByKey(report.getString("command"));
36       command.execute(msg,user,room);
37     }
38 
39 }

 通过JSONObject report = JSONObject.fromObject(msg)就可以将字符串转换为json对象。也就等于通过websocket实现了实时对象信息的传递。

在前端页面中只需要页面载入时的js中加入

1 $(function() {                
2         roomsocket = new WebSocket('ws://127.0.0.1:8080/XXXX/room);
3 }

 在前端向后台发送时将数据JSON.stringify(data)就能json化。

上述代码做了不少删减。所以代码不见得复制就能用。只是提供一种解决方案。况且不久java新的版本或者说Spring Framework 4.0就能很轻松的支持了websocket的实现。主要是还是给自己这段时间的websocket研究做一些总结。随着各种新的技术的诞生,实时web技术已经越来越成熟。websocket是html5的重要特色是值得去看看,研究一下。

 

posted @ 2013-06-05 21:09  Lucious  阅读(3759)  评论(2编辑  收藏  举报