一、SpringBoot

1.1 创建方式

方式一:通过官方网站创建项目、下载导入到开发工具中

官方链接:https://start.spring.io/

阿里链接:https://start.aliyun.com/bootstrap.html?spm=a2ck6.17690074.0.0.503c5bb4WGesvy

官方创建示例:

55 - SpringBoot、WebSocket - 图1

下载到本地

55 - SpringBoot、WebSocket - 图2

方式二:通过IDEA开发工具创建

File->new Project

55 - SpringBoot、WebSocket - 图3

55 - SpringBoot、WebSocket - 图4

55 - SpringBoot、WebSocket - 图5

55 - SpringBoot、WebSocket - 图6

1.2 项目主启动类

  • SpringBoot项目都有一个默认的主启动类,此类不能够删除
  • @SpringBootApplication注解表示当前类为主启动类,而且会自动扫描当前主类所在的包及其子包,因此我们自定义的包、代码都应该放在该包下,这样就不用我们自己去扫描对应的包
  1. @SpringBootApplication
  2. public class EducationApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(EducationApplication.class, args);
  5. }
  6. }

如果自定义的包、代码不在主启动类所在包下,也可以在主启动类上通过@ComponentScan扫描指定的包

  1. @SpringBootApplication
  2. @ComponentScan("com.controller")
  3. public class EducationApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(EducationApplication.class, args);
  6. }
  7. }

1.3 整合mybatis

1、在pom.xml引入依赖

  1. <dependency>
  2. <groupId>org.mybatis.spring.boot</groupId>
  3. <artifactId>mybatis-spring-boot-starter</artifactId>
  4. <version>2.1.4</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>mysql</groupId>
  8. <artifactId>mysql-connector-java</artifactId>
  9. <version>8.0.15</version>
  10. </dependency>

2、在application.properties文件中配置mybatis需要的参数

# 加载mybatis主配置文件
mybatis.config-location=classpath:mybatis/mybatis-config.xml
# 加载mapper.xml文件
mybatis.mapper-locations=classpath:mybatis/mappers/*.xml
# 取别名
mybatis.type-aliases-package=com.woniuxy.entity

# 链接数据库的基本参数
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/k15?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456

3、在项目的resources目录(相当于web项目中的src)下创建mybatis目录,将mybatis-config.xml文件放在该目录下,并且在该目录下创建mappers目录,项目中使用到的所有mapper文件都放在该目录下面。

4、在Mapper接口上添加@Mapper注解,表明当前接口是一个mapper

@Mapper //来自mybatis  表示当前接口是一个mapper
public interface MenuMapper {
    @Select("select rid from user_role where account = #{account}") //accout rid
    public List<Integer> findRidAccount(String account);

    @SelectProvider(type=MenuMapperProvider.class,method="findMenuid")
    public List<Integer> findMenuIdByRids(List<Integer> rids); //动态SQL

    @SelectProvider(type=MenuMapperProvider.class,method="findMenuByMids")
    public List<Menu> findMenuByMids(List<Integer> mids);
}

5、由于@Mapper注解是mybatis的注解,因此通过主启动类扫描时并不能够识别该注解,需要我们自己在主启动类上通过@MapperScan注解进行扫描

@SpringBootApplication
@MapperScan("com.woniuxy.mapper")  //扫描mapper
public class Education2Application {

    public static void main(String[] args) {
        //运行项目,将项目部署到内置Tomcat中
        SpringApplication.run(Education2Application.class, args);
    }

}

6、测试:运行项目发送请求

1.4 关于application主配置文件

application主配置文件有两种格式:properties和yml,yml相对于properties更加简洁直观,springboot项目也可以使用yml文件作为主配置文件。

将application.properties文件改名为application.yml,重新编写配置内容

mybatis:
 config-location: classpath:mybatis/mybatis-config.xml
 mapper-locations: classpath:mybatis/mappers/*.xml
 type-aliases-package: com.woniuxy.entity

spring:
 datasource:
  driver-class-name: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost:3306/k15?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
  username: root
  password: 123456

注意:冒号后面需要一个”空格”,每行缩进不要使用tab键,应该使用空格进行缩进

1.5 数据校验

在项目中为了保证系统安全,经常需要对用户填写的数据进行校验,校验分为前端校验和后端校验,而前端校验一般的作用是校验数据在填写是是否合法,但是此种方式并不能保证系统的安全性,因此在后端进行数据校验是必需的。

步骤:

1、在需要进行数据校验的Entity实体类上添加相关校验注解

@Data
public class User {
    @NotBlank(message = "账号不能为空")  //不能为null和""
    private String account;
    @Size(min = 6,max = 10,message = "密码必须在6-10位之间")
    private String pwd;
    @Email(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$"
            ,message = "邮箱格式不正确")
    private String email;
    @Pattern(regexp = "^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\\d{8}$",
            message = "手机格式不正确")
    private String telPhone;
}

说明:

  • @NotBlank 不为null和””
    • message 格式不对时的提示消息
  • @Size指定字符串的长度
    • min 最小长度
    • max 最大长度
  • @Email 校验是否为email
    • regexp 进行校验的正则表达式
  • @Pattern 自定义正则校验,通常用来校验比较复杂的字符串

2、在需要进行数据校验的controller方法的参数前添加@Validated注解,并添加BindingResult参数

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/regist")
    @ResponseBody
    public String regist(@Validated User user, BindingResult result){
        //当请求到该方法时,标准了@Validated注解,那么springboot就会对user对象进行数据校验
        //校验规则在User实体类的属性上指定,校验完毕之后会将结果封装到BindingResult对象中,然后
        //以参数方式传入到该方法中
        //判断
        if (result.hasErrors()){
            List<FieldError> errors = result.getFieldErrors();//获取到所有的错误信息
            System.out.println(errors);
            for (FieldError error : errors) {
                System.out.println(error.getDefaultMessage());//获取错误信息
            }
            //如果有错误信息,向前端返回错误信息  JSON数据
        }
        return "success";
    }
}

运行原理:只要请求到regist方法,程序看到参数user前有@Validated注解,那么就会自动根据User类中指定的校验规则对提交的数据进行校验,然后将校验的结果封装到BindingResult对象中,并将该对象以参数的方式传入regist方法,因此可以在该方法中通过判断是否有错误信息得到对应的校验结果。

二、WebSocket

2.1 WebSocket简介

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,可以在html页面直接使用。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

过去,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

55 - SpringBoot、WebSocket - 图7
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过TCP连接直接交换数据。

当获取WebSocket连接后,可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

WebSocket在传输的过程中不再使用http协议,而是Stomp协议

STOMP

STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。

STOMP协议的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。

STOMP是一个非常简单和容易实现的协议,其设计灵感源自于HTTP的简单性。尽管STOMP协议在服务器端的实现可能有一定的难度,但客户端的实现却很容易。例如,可以使用Telnet登录到任何的STOMP代理,并与STOMP代理进行交互。

2.2 WebSocket 事件

以下是 WebSocket 对象的相关事件。

事件 事件处理程序 描述
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

2.3 WebSocket 方法

以下是 WebSocket 对象的相关方法。

方法 描述
Socket.send() 使用连接发送数据
Socket.close() 关闭连接

2.4 WebSocket 开发步骤

1、在pom.xml中引入websocket相关包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2、在static目录下创建index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" type="text/css" href="css/index.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript">
    $(document).ready(function(){
        var url = "ws://localhost:8080/WebSocketHandler/";
        var ws = null;
        //连接服务器
        $("#join").click(function(){
            var userName = $("#userName").val();
            url = url + userName;    //传递用户名
            console.info(url);
            //创建对象,连接服务器
            ws = new WebSocket(url);     //html5中提供了    

            //给open事件绑定方法
            ws.onopen = function(){
                console.info("连接成功");
            }
            //接收到数据
            ws.onmessage = function(result){
                var textarea = document.getElementById('message');
                textarea.append(result.data+"\n");
                 //将文本域的滚动条滚动到最后
                textarea.scrollTop = textarea.scrollHeight;
            }
            //关闭连接
            ws.onclose = function(){
                $("#message").append("用户:"+userName+"离开聊天室"+"\n");
                console.info("关闭连接");
            }
        });
        //发送消息
        $("#send").click(function(){
            //将输入框中的消息发送给服务器,并且显示到消息框中
            var messageInput = $("#textarea");
            var message = messageInput.val();
            if(ws!=null){
                ws.send(message);    //发送消息
                messageInput.val("");
            }
        });
        //断开连接
        $("#out").click(function(){
            if(ws!=null){
                ws.close();    
            }
        });

    })
</script>
</head>
<body>
    <div id="box">
        <p>聊天室</p>
        <textarea rows="10" cols="50" disabled="disabled" id="textarea"></textarea><br>
        <div class="infoBox">
            用户名:<input type="text" id="userName"><br><br>
            <button style="color: green;" id="join">加入聊天室</button>
            &nbsp;&nbsp;&nbsp;&nbsp;
            <button style="color: red;" id="out">离开聊天室</button>
        </div>
        <br><br>
        <div class="infoBox">
            消&nbsp;&nbsp;&nbsp;息:<input type="text" id="message"><br><br>
            <button id="send">发送消息</button>
        </div>
        <br>

    </div>
</body>
</html>

3、在static下创建css目录,并在该目录下创建index.css

#box{
    width: 500px;
    background: pink;
    text-align: center;
}
.infoBox{
    text-align:left;
    position: relative;
    left: 62px;
}
#message{
    width: 322px;
}
#send{
    position:relative;
    left:50px;
    height:30px;
    width: 326px;
}

4、创建WebSocketUtil工具类用于向浏览器发送消息

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.websocket.Session;
import javax.websocket.RemoteEndpoint.Basic;
public class WebSocketUtil {
    //HashMap:不支持多线程,并发情况下线程不安全
    public static final Map<String, Session> MESSAGEMAP = new ConcurrentHashMap<>();

    //发送消息给客户端
    public static void sendMessage(Session session,String message) {
        if (session!=null) {
            final RemoteEndpoint.Basic basic = session.getBasicRemote();
            if (basic!=null) {
                try {
                    basic.sendText(message);//发送消息回客户端
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //将消息给所有聊天室的人
    //循环发送
    public static void sendMessageToAll(String message) {
        MESSAGEMAP.forEach((sessionId,session)->sendMessage(session, message));
    }
}

5、创建用于接受前台消息的handler

import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.web.bind.annotation.RestController;
import com.my.utils.WebSocketUtil;

@RestController
@ServerEndpoint("/WebSocketHandler/{userName}")        //表示接受的是STOMP协议提交的数据
public class WebSocketHandler {

    //建立连接
    @OnOpen
    public void openSession(@PathParam("userName")String userName,Session session) {
        //消息
        String message = "欢迎:"+userName+"加入群聊";
        //加入聊天室
        WebSocketUtil.MESSAGEMAP.put(userName, session);
        //发送消息
        WebSocketUtil.sendMessageToAll(message);
    }

    @OnMessage
    public void onMessage(@PathParam("userName")String userName,String message) {
        message = userName+":"+message;
        WebSocketUtil.sendMessageToAll(message);
    }

    //离开聊天室
    @OnClose
    public void onClose(@PathParam("userName")String userName,Session session) {
        //将当前用户从map中移除 注销
        WebSocketUtil.MESSAGEMAP.remove(userName);
        //群发消息
        WebSocketUtil.sendMessageToAll("用户:"+userName+"离开聊天室");
        //关闭session
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //连接异常
    @OnError
    public void onError(Session session,Throwable throwable) {
        try {
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6、在SpringBoot主启动类上开启websocket,并配置发送消息时需要的bean

@SpringBootApplication
@EnableWebSocket    //启用websocket
public class SpringBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootTest2Application.class, args);
    }

    @Bean    //在容器中创建bean对象,在WebSocketUtil中需要用到的RemoteEndpoint对象
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

7、运行项目测试