web聊天室项目开发过程及重难点整理
目录
- 一、需求分析
- 二、业务背景
- 1.张三要发消息给李四
- 2.WebSocket实现消息推送流程
- 三、前后端接口和数据库系统设计
- 1.用户相关的接口
- 2.频道相关接口
- 3.数据库表的设计
- 四、功能交互实现原理及代码展示
- 1.输入url访问主页
- 2.调用检查登陆状态接口
- 2.1参数ok:true时显示登录信息
- 3.响应ok:false不包含用户信息显示未登录界面
- 4.点击登录按钮显示登录框
- 5.输入账号密码,点击登录按钮调用login接口,创建session
- 6.参数ok:true表示登陆成功跳转至频道列表页面
- 7.查找频道信息初始化websocket
- 7.1查找频道接口
- 7.2调用初始化websocket方法,筛选频道
- 8.历史记录和新消息的区别
- 五、开发过程及结果展示
- 1.工具类Util类开发Json序列化与数据库操作
- 2.model层实体类设计与数据库相关联
- 2.1用户实体类
- 2.2消息实体类
- 2.3频道实体类
- 2.4信息返回实体类
- 3.service层根据前后端接口编写Servlet
- 3.1登录功能开发
- 3.1.1根据前端登录接口调用LoginServlet
- 3.1.2LoginServlet调用UserDAO操作数据库验证用户合法性
- 3.2登陆成功后查找频道信息功能
- 3.2.1前端跳转接口调用ChannelServlet
- 3.2.2ChannelServlet调用ChannelDAO查询
- 3.3频道进入后进行消息发送和接收
- 3.3.1MessageWebsocketServlet关联TestWebSocket
- 3.3.2调用MessageDAO层操作数据库
- 3.4退出登录功能
- 3.4.1前端接口跳转执行LogoutServlet
- 3.4.2LogoutServlet删除session会话信息并进行更新登陆时间操作
- 六、项目开发重难点
- 1.Session会话
- 2.ajax
- 4.涉及Web技术
- 5.认识WebSocket
- 6.序列化与反序列化
- 7.JDBC操作
- 七、项目亮点
- 1.阻塞队列
- 2.双重校验锁
一、需求分析
1.登录:同一个浏览器,即使多个标签页,保持相同的session。
2.对应消息频道可以收发消息,不同的消息频道不能收发。
3.历史记录查看,新登陆用户可以查看退出登陆后的消息
二、业务背景
1.张三要发消息给李四
实现
1.客户端点到点发消息
2.服务端转发消息(本项目方法)
使用http协议是不可以的因为
服务端的IP是开放在公网的,所有人都能访问
客户端的IP是不开放的
2.WebSocket实现消息推送流程
一.客户端和服务器端建立连接,只能客户发起请求,双方建立链接。
//客户端js代码
websocket = new WebSocket("ws://localhost:8080/java_chatroom/test/1");
1.1双方建立连接(双方通知打开连接的回调函数执行)
1.2客户端建立连接回调
//连接成功建立的回调方法websocket.onopen = function(event){setMessageInnerHTML("open");}
1.3服务端建立连接回调,同时建立长连接Session
@OnOpenpublic void onOpen(@PathParam("userId") String userId, Session session) {this.userId = userId;System.out.println("打开连接: " + userId);}
二、双方的通信都是全双工,客户端和服务端都可以主动收发消息
2.1客户端发送消息
//发送消息function send(){var message = document.getElementById('text').value;websocket.send(message);}
2.2服务端发送消息
@OnMessagepublic void onMessage(String message, Session session) throws IOException {System.out.println("收到消息! " + userId + ": " + message);session.getBasicRemote().sendText(message);}
建立连接以后,就会有的会话对象,只要有sesiion引用就可以发消息,区别于Servlet的session
三、双方接收消息:被动接受(事件驱动的异步回调)
3.1:客户端接收消息
//接收到消息的回调方法websocket.onmessage = function(event){setMessageInnerHTML(event.data);}
3.2:服务端接收消息
@OnMessagepublic void onMessage(String message, Session session) throws IOException {System.out.println("收到消息! " + userId + ": " + message);//session.getBasicRemote().sendText(message);}
三、前后端接口和数据库系统设计
1.用户相关的接口
1.注册
请求:
POST /register
{name: xxx,password: xxx,nickName: "蔡徐坤",signature: "我擅长唱跳rap篮球", }
响应:
HTTP/1.1 200 OK
{ok: 1,reason: xxx
}
2.登录 输入账号密码,点击登录按钮,调用的接口
请求:
POST /login
{name: xxx,password: xxx
}
响应:
HTTP/1.1 200 OK
{ok: 1,reason: xxx,userId: xxx,name: xxx,nickName: xxx,signature: xxx
}
3.检查登陆状态 页面初始化,调用的接口
请求:
GET /login
响应:
响应:
HTTP/1.1 200 OK
{ok: 1,userId: xxx,name: xxx,nickName: xxx,signature: xxx
}
注销
请求:
GET /logout
响应:
HTTP/1.1 200 OK
{ok: 1,reason: xxx
}
2.频道相关接口
查找频道
请求:
GET /channel
响应:
HTTP/1.1 200 OK
[{channelId: 1,channelName: xxx},{channelId: 2,channelName: xxx}
]
3.数据库表的设计
user表
create table user (userId int primary key auto_increment,name varchar(50) unique,password varchar(50),nickName varchar(50), -- 昵称iconPath varchar(2048), -- 头像路径signature varchar(100),lastLogout DateTime -- 上次登录时间
); -- 个性签名
频道表
create table channel (channelId int primary key auto_increment,channelName varchar(50)
);
insert into channel values(null, '体坛赛事');
insert into channel values(null, '娱乐八卦');
insert into channel values(null, '时事新闻');
insert into channel values(null, '午夜情感');
信息表
create table message (messageId int primary key auto_increment,userId int, -- 谁发的channelId int, -- 发到哪个频道中content text, -- 消息内容sendTime DateTime default now() -- 发送时间
);insert into message values (null, 1, 1, 'hehe1', now());
insert into message values (null, 1, 1, 'hehe2', now());
insert into message values (null, 1, 1, 'hehe3', now());
四、功能交互实现原理及代码展示
1.输入url访问主页
2.调用检查登陆状态接口
2.1参数ok:true时显示登录信息
3.响应ok:false不包含用户信息显示未登录界面
4.点击登录按钮显示登录框
5.输入账号密码,点击登录按钮调用login接口,创建session
请求:
POST /login
{name: xxx,password: xxx
}
响应:
HTTP/1.1 200 OK
{ok: 1,reason: xxx,userId: xxx,name: xxx,nickName: xxx,signature: xxx
}
6.参数ok:true表示登陆成功跳转至频道列表页面
7.查找频道信息初始化websocket
app.getChannels();app.initWebSocket();
7.1查找频道接口
请求:
GET /channel
响应:
HTTP/1.1 200 OK
[{channelId: 1,channelName: xxx},{channelId: 2,channelName: xxx}
]
7.2调用初始化websocket方法,筛选频道
if('WebSocket' in window){this.websocket = new WebSocket("ws://localhost:8080/java_chatroom/message/" + this.user.userId);console.log("link success")}else{alert('Not support websocket')}//连接发生错误的回调方法this.websocket.onerror = function () {alert("连接发生错误!");};//连接成功建立的回调方法this.websocket.onopen = function (event) {console.log("连接建立成功");}//接收到消息的回调方法this.websocket.onmessage = function (event) {let message = JSON.parse(event.data);app.messages.push(message);//筛选频道app.curChannelMessages = app.messages.filter((message, i) => {if (message.channelId == app.curChannelId) {return true;}return false;});}//连接关闭的回调方法this.websocket.onclose = function () {console.log("服务器断开连接");}
8.历史记录和新消息的区别
五、开发过程及结果展示
1.工具类Util类开发Json序列化与数据库操作
public class Util {//序列化private static final ObjectMapper M = new ObjectMapper();//数据库连接池private static final MysqlDataSource DS = new MysqlDataSource();//初始化static {//设置json序列化/反序列化的日期格式DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");M.setDateFormat(df);DS.setURL("jdbc:mysql://localhost:3306/java_chatroom");DS.setUser("root");DS.setPassword("111111");DS.setUseSSL(false);DS.setCharacterEncoding("UTF-8");//解决插入修改数据,如果是中文,乱码的问题}/*** json序列化:java对象转换为json字符串*/public static String serialize(Object o){try {return M.writeValueAsString(o);} catch (JsonProcessingException e) {throw new AppException("json序列化失败:"+o, e);}}/*** 反序列化:把json字符串转换为java对象*/public static <T> T deserialize(String s, Class<T> c){try {return M.readValue(s, c);} catch (JsonProcessingException e) {//如果出现这个异常,一般都是json字符串中的键,在class中没有找到对应的属性throw new AppException("json反序列化失败", e);}}public static <T> T deserialize(InputStream is, Class<T> c){try {return M.readValue(is, c);} catch (IOException e) {//如果出现这个异常,一般都是json字符串中的键,在class中没有找到对应的属性throw new AppException("json反序列化失败", e);}}/*** 获取数据库连接*/public static Connection getConnection(){try {return DS.getConnection();} catch (SQLException e) {throw new AppException("获取数据库连接失败", e);}}/*** 释放jdbc资源*/public static void close(Connection c, Statement s, ResultSet r){try {if(r != null) r.close();if(s != null) s.close();if(c != null) c.close();} catch (SQLException e) {throw new AppException("释放数据库资源出错", e);}}public static void close(Connection c, Statement s){close(c, s, null);}public static void main(String[] args) {//测试json序列化Map<String, Object> map = new HashMap<>();map.put("ok", true);map.put("d", new Date());System.out.println(serialize(map));//测试数据库连接: 需要把init.sql在cmd执行,初始化数据库,表,数据System.out.println(getConnection());}
}
2.model层实体类设计与数据库相关联
2.1用户实体类
//数据库使用,前后端ajax,session保存时基于对象和二进制数据转换(这里要实现串行化接口)
@Getter
@Setter
@ToString
public class User extends Response implements Serializable {private static final Long serialVersionUID = 1L;private Integer userId;private String name;private String password;private String nickName;private String iconPath;private String signature;private java.util.Date lastLogout;}
2.2消息实体类
@Setter
@Getter
@ToString
public class Message {private Integer messageId;private Integer userId;private Integer channelId;private String content;private java.util.Date sendTime;//接收客户端发送的消息,转发到所有客户端的消息,需要昵称private String nickName;
}
2.3频道实体类
@ToString
@Getter
@Setter
public class Channel {private Integer channelId;private String channelName;}
2.4信息返回实体类
@Setter
@Getter
@ToString
public class Response {//当前接口响应是否操作成功private boolean ok;//操作失败时,前端要展示的错误信息private String reason;private Object data;//保存业务数据
}
3.service层根据前后端接口编写Servlet
1.请求url,对应Servlet的路径
2.一次init,多次service根据请求方法调用doxxx方法,
3.1登录功能开发
3.1.1根据前端登录接口调用LoginServlet
@WebServlet("/login")
public class LoginServlet extends HttpServlet {//检测登陆状态@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("UTF-8");resp.setCharacterEncoding("UTF-8");resp.setContentType("application/json");//响应的数据:根据接口文档,user类中都包含了约定的字段User u = new User();//获取当前请求的session,并再获取用户信息,如果获取不到,返回ok:falseHttpSession session = req.getSession(false);if(session != null){User get = (User) session.getAttribute("user");if(get != null){//已经登录,并获取到用户信息u = get;u.setOk(true);resp.getWriter().println(Util.serialize(u));return;}}u.setOk(false);//其实不用设置,该字段为boolean,默认就是falseu.setReason("用户未登陆");//3 返回响应数据: 从响应对象获取输出流,打印输出到响应体bodyresp.getWriter().println(Util.serialize(u));}//登录接口@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("UTF-8");resp.setCharacterEncoding("UTF-8");resp.setContentType("application/json");//响应的数据:根据接口文档,user类中都包含了约定的字段User u = new User();try {//1.解析请求数据:根据接口文档,需要使用反序列化操作User input = Util.deserialize(req.getInputStream(), User.class);//2.业务处理: 数据库验证账号密码,如果验证通过,创建session,保存用户信息//根据账号查询用户User query = UserDAO.queryByName(input.getName());if(query == null){throw new AppException("用户不存在");}if(!query.getPassword().equals(input.getPassword())){throw new AppException("账号或密码错误");}//账号密码验证成功HttpSession session = req.getSession();session.setAttribute("user", query);u = query;//构造操作成功正常返回数据:ok:true, 业务字段u.setOk(true);}catch (Exception e){e.printStackTrace();//构造操作失败的错误信息 ok;false reason:错误信息。u.setOk(false);//自定义异常,自己抛,为中文信息,可以给用户看if(e instanceof AppException){u.setReason(e.getMessage());}else{//非自定义异常,英文信息,转一下u.setReason("未知的错误,请联系管理员");}}//3.返回响应数据: 从响应对象获取输出流,打印输出到响应体bodyresp.getWriter().println(Util.serialize(u));}
}
3.1.2LoginServlet调用UserDAO操作数据库验证用户合法性
public static User queryByName(String name) {Connection c = null;PreparedStatement ps = null;ResultSet rs = null;//定义返回数据User u = null;try {//1 获取数据库连接Connectionc = Util.getConnection();//2 通过Connection+sql创建操作命令对象StatementString sql = "select * from user where name=?";ps = c.prepareStatement(sql);//3 执行sql: 执行前替换占位符ps.setString(1, name);rs = ps.executeQuery();//4 如果是查询操作,处理结果集while (rs.next()){//移动到下一行,有数据返回trueu = new User();//设置结果集字段到用户对象的属性中u.setUserId(rs.getInt("userId"));u.setName(name);u.setPassword(rs.getString("password"));u.setNickName(rs.getString("nickName"));u.setIconPath(rs.getString("iconPath"));u.setSignature(rs.getString("signature"));java.sql.Timestamp lastLogout = rs.getTimestamp("lastLogout");u.setLastLogout(new Date(lastLogout.getTime()));}return u;}catch (Exception e){throw new AppException("查询用户账号出错", e);}finally {//5 释放资源Util.close(c, ps, rs);}}
3.2登陆成功后查找频道信息功能
3.2.1前端跳转接口调用ChannelServlet
@WebServlet("/channel")
public class ChannelServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("UTF-8");resp.setCharacterEncoding("UTF-8");resp.setContentType("application/json");Response response = new Response();try{//查询所有频道列表返回List<Channel> list = ChannelDAO.query();response.setOk(true);response.setData(list);//ok:true, data: [{}, {}]}catch (Exception e){e.printStackTrace();//目前,前端的实现,在后端报错,要返回空的List//改造前端为解析ok, reason//参考LoginServlet改造:response.setReason(e.getMessage());//ok:false, reason: ""}resp.getWriter().println(Util.serialize(response));}
}
3.2.2ChannelServlet调用ChannelDAO查询
public class ChannelDAO {public static List<Channel> query() {Connection c = null;PreparedStatement ps = null;ResultSet rs = null;//定义返回数据List<Channel> list = new ArrayList<>();try {//1 获取数据库连接Connectionc = Util.getConnection();//2 通过Connection+sql创建操作命令对象StatementString sql = "select * from channel";ps = c.prepareStatement(sql);//3 执行sql: 执行前替换占位符rs = ps.executeQuery();//4 如果是查询操作,处理结果集while (rs.next()){//移动到下一行,有数据返回trueChannel channel = new Channel();//设置属性channel.setChannelId(rs.getInt("channelId"));channel.setChannelName(rs.getString("channelName"));list.add(channel);}return list;}catch (Exception e){throw new AppException("查询频道列表出错", e);}finally {//5 释放资源Util.close(c, ps, rs);}}
}
3.3频道进入后进行消息发送和接收
3.3.1MessageWebsocketServlet关联TestWebSocket
@ServerEndpoint("/message/{userId}")
public class MessageWebsocket {@OnOpenpublic void onOpen(@PathParam("userId") Integer userId,Session session) throws IOException {//1.把每个客户端的session都保存起来,之后转发消息到所有客户端要用MessageCenter.addOnlineUser(userId, session);//2.查询本客户端(用户)上次登录前的消息(数据库查)List<Message> list = MessageDAO.queryByLastLogout(userId);//3.发送当前用户在上次登录后的消息for(Message m : list){session.getBasicRemote().sendText(Util.serialize(m));}System.out.println("建立连接:"+userId);}@OnMessagepublic void onMessage(Session session,String message){//1.遍历保存的所有session,每个都发送消息
// MessageCenter.sendMessage(message);MessageCenter.getInstance().addMessage(message);//2.消息还要保存在数据库:// (1)反序列化json字符串为message对象Message msg = Util.deserialize(message, Message.class);// (2)插入数据库int n = MessageDAO.insert(msg);System.out.printf("接收到消息:%s\n", message);}@OnClosepublic void onClose(@PathParam("userId") Integer userId){//1.本客户端关闭连接,要在之前保存的session集合中,删除MessageCenter.delOnlineUser(userId);//2.建立连接要获取用户上次登录以后的消息,所以关闭长连接就是代表用户退出//更新用户的上次登录时间int n = UserDAO.updateLastLogout(userId);System.out.println("关闭连接");}@OnErrorpublic void onError(@PathParam("userId") Integer userId, Throwable t){System.out.println("出错了");MessageCenter.delOnlineUser(userId);t.printStackTrace();//和关闭连接的操作一样}}
3.3.2调用MessageDAO层操作数据库
public static int insert(Message msg) {Connection c = null;PreparedStatement ps = null;try{c = Util.getConnection();String sql = "insert into message values(null,?,?,?,?)";ps = c.prepareStatement(sql);ps.setInt(1, msg.getUserId());ps.setInt(2, msg.getChannelId());ps.setString(3, msg.getContent());ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));return ps.executeUpdate();}catch (Exception e){throw new AppException("保存消息出错", e);}finally {Util.close(c, ps);}}public static List<Message> queryByLastLogout(Integer userId) {Connection c = null;PreparedStatement ps = null;ResultSet rs = null;//定义返回数据List<Message> list = new ArrayList<>();try {//1 获取数据库连接Connectionc = Util.getConnection();//2 通过Connection+sql创建操作命令对象StatementString sql = "select m.*,u.nickName from message m join user u on u.userId=m.userId where m.sendTime>(select lastLogout from user where userId=?)";ps = c.prepareStatement(sql);//3 执行sql: 执行前替换占位符ps.setInt(1, userId);rs = ps.executeQuery();//4 如果是查询操作,处理结果集while (rs.next()){//移动到下一行,有数据返回trueMessage m = new Message();//获取结果集字段,设置对象属性m.setUserId(userId);m.setNickName(rs.getString("nickName"));m.setContent(rs.getString("content"));m.setChannelId(rs.getInt("channelId"));list.add(m);}return list;}catch (Exception e){throw new AppException("查询用户["+userId+"]的消息出错", e);}finally {//5 释放资源Util.close(c, ps, rs);}}
}
3.4退出登录功能
3.4.1前端接口跳转执行LogoutServlet
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("UTF-8");resp.setCharacterEncoding("UTF-8");resp.setContentType("application/json");HttpSession session = req.getSession(false);if(session != null){User user = (User) session.getAttribute("user");if(user != null){//用户已登录:删除session中保存的用户信息(注销)session.removeAttribute("user");//注销成功,返回ok: trueResponse r = new Response();r.setOk(true);resp.getWriter().println(Util.serialize(r));return;}}//用户未登陆Response r = new Response();r.setReason("用户未登陆,不允许访问");resp.getWriter().println(Util.serialize(r));}
}
3.4.2LogoutServlet删除session会话信息并进行更新登陆时间操作
public static int updateLastLogout(Integer userId) {Connection c = null;PreparedStatement ps = null;try{c = Util.getConnection();String sql = "update user set lastLogout=? where userId=?";ps = c.prepareStatement(sql);ps.setTimestamp(1, new Timestamp(System.currentTimeMillis()));ps.setInt(2, userId);return ps.executeUpdate();}catch (Exception e){throw new AppException("修改用户上次登录时间出错", e);}finally {Util.close(c, ps);}}
六、项目开发重难点
1.Session会话
Session:登陆后,注销前或超时前都是一个会话,用来解决未登录的敏感资源访问问题
代码:
登陆时:账号密码验证通过后request.getSession();
登陆后,都可以获取到Session对象
2.ajax
异步请求回调(等请求的过程中干别的)
过程:
1.HTTP请求数据:路径,请求方法,请求数据
2.通过路径调用后端Servlet中的相关类,找到对应的请求方法doPost
3.对象序列化为json字符串
4.异步回掉函数,不是傻乎乎的等
//接口:
请求:
POST /login
{name: xxx,password: xxx
}
响应:
HTTP/1.1 200 OK
{ok: 1,reason: xxx,userId: xxx,name: xxx,nickName: xxx,signature: xxx
}
//前端ajax代码——登录功能
loginAccount() {console.log("login");$.ajax({url: 'login',type: 'post',contentType: 'application/json',data: JSON.stringify({name: app.login.inputUsername,password: app.login.inputPassword,}),success: function(data, status) {if (!data.ok) {alert('登陆失败! ' + data.reason);app.login.isLogin = false;return;}app.user.userId = data.userId;app.user.name = data.name;app.user.nickName = data.nickName;app.user.signature = data.signature;app.login.isLogin = true;app.login.showLoginDialog = false;app.getChannels();app.initWebSocket();}})},
4.涉及Web技术
1.开发流程:开发——打包——部署——运行——验证
打包:maven package
部署:复制war文件到tomcat/webapps下
运行:运行tomcat自动解压webapps目录下的war文件,每个文件夹就是一个web项目(应用上下文路径)
2.IDEA部署Tomcat应用上下文路径保持一致。
5.认识WebSocket
服务端主动向客户端发送。
消息推送方法:
轮询方式:客户端定时向服务端发送ajax请求,服务器接收到请求后马上返回消息并关闭连接。
优点:后端程序编写比较容易。
缺点:TCP的建立和关闭操作浪费时间和带宽,请求中有大半是无用,浪费带宽和服务器资源。
实例:适于小型应用。
长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息
并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。
优点:在无消息的情况下不会频繁的请求,耗费资源小。
缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。
实例:WebQQ、Hi网页版、Facebook IM。
长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用
xhr请求,服务器端就能源源不断地往客户端输入数据。
优点:消息即时到达,不发无用请求;管理起来也相对方便。
缺点:服务器维护一个长连接会增加开销,当客户端越来越多的时候,server压力大!
实例:Gmail聊天
webSocket:HTML5 WebSocket设计出来的目的就是取代轮询和长连接,使客户端浏览器具备像C/S
框架下桌面系统的即时通讯能力,实现了浏览器和服务器全双工通信,建立在TCP之上,虽然
WebSocket和HTTP一样通过TCP来传输数据,但WebSocket可以主动的向对方发送或接收数据,就像
Socket一样;并且WebSocket需要类似TCP的客户端和服务端通过握手连接,连接成功后才能互相通
信。
优点:双向通信、事件驱动、异步、使用ws或wss协议的客户端能够真正实现意义上的推送功能。
缺点:少部分浏览器不支持。
示例:社交聊天(微信、QQ)、弹幕、多玩家玩游戏、协同编辑、股票基金实时报价、体育实况更
新、视频会议/聊天、基于位置的应用、在线教育、智能家居等高实时性的场景。
6.序列化与反序列化
public class Util {//序列化private static final ObjectMapper M = new ObjectMapper();//数据库连接池private static final MysqlDataSource DS = new MysqlDataSource();//初始化static {//设置json序列化/反序列化的日期格式DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");M.setDateFormat(df);DS.setURL("jdbc:mysql://localhost:3306/java_chatroom");DS.setUser("root");DS.setPassword("111111");DS.setUseSSL(false);DS.setCharacterEncoding("UTF-8");//解决插入修改数据,如果是中文,乱码的问题}/*** json序列化:java对象转换为json字符串*/public static String serialize(Object o){try {return M.writeValueAsString(o);} catch (JsonProcessingException e) {throw new AppException("json序列化失败:"+o, e);}}/*** 反序列化:把json字符串转换为java对象*/public static <T> T deserialize(String s, Class<T> c){try {return M.readValue(s, c);} catch (JsonProcessingException e) {//如果出现这个异常,一般都是json字符串中的键,在class中没有找到对应的属性throw new AppException("json反序列化失败", e);}}public static <T> T deserialize(InputStream is, Class<T> c){try {return M.readValue(is, c);} catch (IOException e) {//如果出现这个异常,一般都是json字符串中的键,在class中没有找到对应的属性throw new AppException("json反序列化失败", e);}}
}
7.JDBC操作
1.占位符替换:使用变量,对象中的属性
2.查询操作,返回的结果集,多行数据转换为list,一行数据转换为一个对象
七、项目亮点
1.阻塞队列
2.双重校验锁
web聊天室项目开发过程及重难点整理相关推荐
- 【软件测试】使用selenium工具对Web聊天室项目进行UI自动化测试
文章目录 用户注册功能 注册成功 注册失败 测试套件 HTML测试报告 异常截图 用户登陆注册功能 登陆成功 账号不存在,登陆失败 密码错误,登陆失败 测试套件 HTML测试报告 异常截图 本次自动化 ...
- 听书项目开发过程及重难点总结(用户管理)
文章目录 一.需求分析 二.前后端交互 1. 注册 **前端** **后端** 2.登录 前端 后端 三.项目开发重难点 1.编写一个Servlet 2.session和session会话机制 1)S ...
- 聊天室项目开发过程总结
项目介绍 该项目简单模仿QQ的聊天模块.项目功能模块包括用户注册.登陆.点击私聊和创建群聊 项目详情 为提升用户体验采用Druid数据源.使用Swing技术开发用户界面.对于客户端服务端的通信采用套接 ...
- 使用nodejs搭建你自己的专属web聊天室
前言 前断时间在学习nodejs,自己闲来无事,在网上搜索了一些资料自己搭建了一个属于自己的web聊天室项目.现在把自己的开发过程和心得和大家分享,希望其中涉及到的一些知识对你有用. 项目开源地址:h ...
- Django项目--web聊天室
需求 做一个web聊天室,主要练习前端ajax与后台的交互: 一对一聊天和群组聊天 添加用户为好友 搜索并添加群组 管理员可以审批用户加群请求,群管理员可以有多个,群管理员可以删除,添加禁言群友 与聊 ...
- ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(一) 之 基层数据搭建,让数据活起来(数据获取)...
大家好,本篇是接上一篇 ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言 ASP.NET SignalR WebIM系列第二篇.本篇会带领大家将 LayIM ...
- LayIM 3.9.1与ASP.NET SignalR实现Web聊天室快速入门(四)之ASP.NET SignalR核心功能介绍
前言 本系列文章特点:使用ASP.NET SignalR和LayIM快速入门对接,实现一对一聊天,群聊,添加聊天群组,查找聊天记录等功能.源代码不包含LayIM的源代码,因为官方并没开源属于收费资源, ...
- 御神楽的学习记录之Springboot+netty实现Web聊天室
文章目录 前言 一.Netty简介 1.介绍 二.Web聊天室实现 1.Idea项目创建 2.java类编写 3.html测试 总结 参考 前言 WebSocket是一种在单个TCP连接上进行全双工通 ...
- 使用FastHttpApi构建多人Web聊天室
为什么80%的码农都做不了架构师?>>> 一般在dotnet core下构建使用web服务应用都使用asp.net core,但通过FastHttpApi组建也可以方便地构建w ...
最新文章
- python二叉树遍历算法_分享python实现的二叉树定义与遍历
- java虚拟机栈帧_Java虚拟机,运行时栈帧结构
- 中医点滴 2 --- 保和丸 + 口气重
- 小米扫地机器人 自动关机_小米扫地机器人1S 真的好用吗?
- hadoop商业版本选择对比
- JVM发生频繁 CMS GC,罪魁祸首是这个参数!
- Python的dnspython库使用指南
- java quartz 数据库_SpringBoot+Quartz+数据库存储
- anaconda来创建python环境
- vue-cli 脚手架移除、安装(最新版安装)、检测安装结果 - npm篇
- 50 - 算法 -二叉树 - 递归 - LeetCode 101
- hdu4405 掷骰子走格子
- ubuntu环境下,ubuntu16.04装机到nvdia显卡驱动安装、cuda8安装、cudnn安装
- 10 LVS负载均衡群集-NAT
- 【图像融合】基于matlab GUI拉普拉斯金字塔+小波变换图像融合【含Matlab源码 857期】
- SSM博客 点赞和文章浏览量实现
- 尚硅谷JVM笔记(宋红康主讲)
- .NetCore获取拼多多平台优惠券
- HTML5期末大作业:商城后台网站设计——网站商城后台通用模板(30页) 大学生后台模板网页作品商城网页设计作业模板 学生网页制作源代码下载
- dota2api的介绍与使用
热门文章
- BUUCTF Url编码
- Opencv:如何调用IP摄像头
- 通过OpenCv的HoughCircles函数获取图片中的圆形
- LeetCode: 173. Binary Search Tree Iterator
- 知云文献翻译打不开_学用系列|自带翻译功能的PDF文献阅读器——知云文献翻译3.0...
- 响铃:跑错了道的VR,如何知途迷返
- 模板的完全特例化和部分特例化
- Javaweb_文件上传
- 机器学习 绘制决策边界
- Python开发【Django】:组合搜索、JSONP、XSS过滤