Spring Boot的web开发(3)

WebSocket
1 WebSocket是什么
WebSocket为浏览器和服务器提供双工异步通信(浏览器可以向服务器发送消息,服务器也可以向浏览器发送消息)。
WebSocket需要浏览器支持。
WebSocket是通过一个socket来实现双工异步通信能力的。
直接使用WebSocket过于麻烦,使用它的子协议STOMP,它是一个更高级别的协议,STOMP协议使用基于帧(frame)的格式来定义消息,与HTTP的request和response类似。
2 Spring Boot提供的自动配置
Spring Boot对内嵌的Tomcat(7、8)、Jetty9和Undertow使用WebSocket提供了支持。
配置源码存于org.springframework.boot.autoconfigure.websocket下.
Spring Boot为WebSocket提供的starter pom是spring-boot-starter-websocket.
3 实战
3.1 准备
新建Spring Boot项目,选择Thymeleaf和Websocket依赖
3.2广播式
广播式即服务端有消息时,会将消息发送给所有连接了当前endpoint的浏览器
(1)配置WebSocket,需要在配置类上使用@EnableWebSocketMessageBroker开启WebSocket支持,并通过集成AbstractWebSocketMessageBrokerConfigurer类,重写其方法来配置WebSocket.
package com.hand;

import org.springframework.context.annotation. Configuration ;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation. EnableWebSocketMessageBroker ;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

/**
* Created by lqy on 2017-11-28.
*/
@Configuration
@EnableWebSocketMessageBroker //通过注解开启使用STOMP协议来传输基于代理(message broker)的消息
//这时控制器支持使用@MessageMapping,就像使用@RequestMapping一样
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{

@Override
public void registerStompEndpoints(StompEndpointRegistry registry){
//注册STOMP协议的节点(endpoint),并映射到指定URL
registry.addEndpoint( "/endpointHand" ).withSockJS(); //注册一个STOMP的endpoint,并指定使用SockJS协议
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry){ //配置消息代理(Message Broker)
registry.enableSimpleBroker( "/topic" ); //广播式应配置一个/topic消息代理
}
}
(2)浏览器向服务端发送的消息用此类接受
package com.hand;

/**
* Created by lqy on 2017-11-28.
*/
public class HandMessage {
private String name ;
public String getName(){
return name ;
}
}
(3)服务端向浏览器发送的此类的消息
package com.hand;

/**
* Created by lqy on 2017-11-28.
*/
public class HandResponse {
private String responseMessage ;
public HandResponse(String responseMessage){
this . responseMessage =responseMessage;
}
public String getResponseMessage(){
return responseMessage ;
}
}
(4)演示控制器:
package com.hand;

import org.springframework.messaging.handler.annotation. MessageMapping ;
import org.springframework.messaging.handler.annotation. SendTo ;
import org.springframework.stereotype. Controller ;

/**
* Created by lqy on 2017-11-28.
*/
@Controller
public class HandController {
@MessageMapping ( "/welcome" ) //当浏览器向服务端发送请求时,通过该注解映射/welcome这个地址,类似@RequestMapping
@SendTo ( "/topic/getResponse" ) //当服务端有消息时,会对订阅了@Sendto中的路径的浏览器发送消息
public HandResponse say(HandMessage message) throws Exception{
Thread. sleep ( 3000 );
return new HandResponse( "welcome," +message.getName()+ "!" );
}
}
(5)添加脚本。
将stomp.min.js(STOMP协议的客户端脚本)、sockjs.min.js(SockJS的客户端脚本)以及jQuery放置在src/main/resources/static下。
(6)演示页面,在src/main/resources/templates下 新建hand.html
<!DOCTYPE html>
< html xmlns: th = "http://www.thymeleaf.org" >
< head >
< meta charset= "UTF-8" />
< title > Spring Boot+WebSocket+广播式 </ title >

</ head >
< body οnlοad= " disconnect () " >
< noscript >< h2 style= " color : #ff0000 " > 貌似你的浏览器不支持websocket </ h2 ></ noscript >
< div >
< div >
< button id= "connect" οnclick= " connect (); " > 连接 </ button >
< button id= "disconnect" disabled= "disabled" οnclick= " disconnect (); " > 断开连接 </ button >
</ div >
< div id= "conversationDiv" >
< label > 输入你的名字 </ label >< input type= "text" id= "name" />
< button id= "sendName" οnclick= " sendName (); " > 发送 </ button >
< p id= "response" ></ p >
</ div >
</ div >
< script th :src= "@{sockjs.min.js}" ></ script >
< script th :src= "@{stomp.min.js}" ></ script >
< script th :src= "@{jquery.js}" ></ script >
< script type= "text/javascript" >
var stompClient = null ;

function setConnected (connected) {
document . getElementById ( 'connect' ). disabled = connected;
document . getElementById ( 'disconnect' ). disabled = !connected;
document . getElementById ( 'conversationDiv' ). style . visibility = connected ? 'visible' : 'hidden' ;
$ ( '#response' ). html ();
}

function connect () {
var socket = new SockJS ( '/endpointHand' ); //连接SockJS的endpoint的名字为“/endpointHand”
stompClient = Stomp . over ( socket ); //使用STOMP子协议的WebSocker客户端
stompClient . connect ({}, function (frame) { //连接WebSocker服务端
setConnected ( true );
console . log ( 'Connected: ' + frame);
stompClient . subscribe ( '/topic/getResponse' , function (respnose){ //通过stompClient.subscribe订阅/topic/getResponse目标(destination)发送的消息,这个是在控制器的@Sento中定义的
showResponse ( JSON . parse (respnose. body ).responseMessage);
});
});
}


function disconnect () {
if ( stompClient != null ) {
stompClient . disconnect ();
}
setConnected ( false );
console . log ( "Disconnected" );
}

function sendName () {
var name = $ ( '#name' ). val ();
//通过stompClient.send向/welcome目标(destination)发送消息,这个是在控制器的MessageMapping中定义的
stompClient . send ( "/welcome" , {}, JSON . stringify ({ 'name' : name }));
}

function showResponse (message) {
var response = $ ( "#response" );
response . html (message);
}
</ script >
</ body >
</ html >

(7)配置viewController,为Hand.html提供便捷的路径映射
package com.hand;

import org.springframework.context.annotation. Configuration ;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
* Created by lqy on 2017-11-28.
*/
@Configuration
public class WebmvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController( "/hand" ).setViewName( "/Hand" );
}
}
(8)运行程序。
预期效果:一个浏览器发送一个消息到服务端时,其他浏览器也能接收到从服务端发送的这个消息。
开启三个浏览器,访问http://localhost:8080/hand,分别连接服务器,然后再浏览器发送一条消息,其他浏览器接收
连接服务端格式:
连接成功的返回:

订阅目标

向目标发送消息的格式

从目标接收的格式

3.3 点对点式
一对一聊天室:
需要用户相关的内容,引入最简单的Spring Security相关内容
(1)添加Spring Security的starter pom;
< dependency >
< groupId > org.springframework.boot </ groupId >
< artifactId > spring-boot-starter-security </ artifactId >
</ dependency >
(2)Spring Security的简单配置.
package com.hand;

import org.springframework.context.annotation. Configuration ;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration. EnableWebSecurity ;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
* Created by lqy on 2017-11-28.
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers( "/" , "/login" ).permitAll() //设置Spring Security对/和/"login"路径不拦截
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage( "/login" ) //设置Spring Security的登陆页面访问的路径为/login
.defaultSuccessUrl( "/chat" ) //登陆成功后转向/chat路径
.permitAll()
.and()
.logout()
.permitAll();
}
//在内存中分别配置两个用户lqy和lqy01,密码与用户名一致,角色是USER
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth
.inMemoryAuthentication()
.withUser( "lqy" ).password( "lqy" ).roles( "USER" )
.and()
.withUser( "lqy01" ).password( "lqy01" ).roles( "USER" );
}
///resources/static目录下的静态资源,Spring Security不拦截
@Override
public void configure(WebSecurity web) throws Exception{
web.ignoring().antMatchers( "/resources/static/**" );
}

}
(3)WebSocket;
package com.hand;

import org.springframework.context.annotation. Configuration ;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation. EnableWebSocketMessageBroker ;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

/**
* Created by lqy on 2017-11-28.
*/
@Configuration
@EnableWebSocketMessageBroker //通过注解开启使用STOMP协议来传输基于代理(message broker)的消息
//这时控制器支持使用@MessageMapping,就像使用@RequestMapping一样
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{

@Override
public void registerStompEndpoints(StompEndpointRegistry registry){
//注册STOMP协议的节点(endpoint),并映射到指定URL
//registry.addEndpoint("/endpointHand").withSockJS();//注册一个STOMP的endpoint,并指定使用SockJS协议
registry.addEndpoint( "/endpointChat" ).withSockJS(); //注册一个STOMP的endpoint,并指定使用SockJS协议
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry){ //配置消息代理(Message Broker)
//registry.enableSimpleBroker("/topic");//广播式应配置一个/topic消息代理
registry.enableSimpleBroker( "/queue" , "/topic" ); //点对点式新增一个/queue消息代理
}
}
(4)控制器
package com.hand;


import org.springframework.beans.factory.annotation. Autowired ;
import org.springframework.messaging.handler.annotation. MessageMapping ;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype. Controller ;

import java.security.Principal;

/**
* Created by lqy on 2017-11-28.
*/
@Controller
public class WebSecurityController {
@Autowired
private SimpMessagingTemplate messagingTemplate ; //通过SimpMessagingTemplate向浏览器发送消息
@MessageMapping ( "/chat" )
public void handleChat(Principal principal, String msg){ //在Spring MVC中,可以直接在参数中获取principal,principal中包含当前用户的信息
if (principal.getName().equals( "lqy" )){ //这是一段硬编码,如果发送人是Lqy,则发送给lqy01,反之。。
//通过messagingTemplate.convertAndSendToUser向用户发送消息,第一个参数是接收消息的用户,第二个是浏览器订阅的地址,第三个是消息本身
messagingTemplate .convertAndSendToUser( "lqy01" , "/queue/notifications" ,principal.getName()+ "-send:" +msg);
} else {
messagingTemplate .convertAndSendToUser( "lqy" , "/queue/notifications" ,principal.getName()+ "-send:" +msg);
}
}

}
(5)登录页面,src/main/resources/templates下新建login.html
<!DOCTYPE html >
< html xmlns= "http://www.w3.org/1999/xhtml" xmlns:th= "http://www.thymeleaf.org"
xmlns: sec = "http://www.thymeleaf.org/thymeleaf-extras-springsecurity3" >
< meta charset= "UTF-8" />
< head >
< title > 登陆页面 </ title >
</ head >
< body >
< div th:if= "${param.error}" >
无效的账号和密码
</ div >
< div th:if= "${param.logout}" >
你已注销
</ div >
< form th:action= "@{/login}" method= "post" >
< div >< label > 账号 : < input type= "text" name= "username" /> </ label ></ div >
< div >< label > 密码 : < input type= "password" name= "password" /> </ label ></ div >
< div >< input type= "submit" value= "登陆" /></ div >
</ form >
</ body >
</ html >
(6)聊天页面
<!DOCTYPE html >

< html xmlns: th = "http://www.thymeleaf.org" >
< meta charset= "UTF-8" />
< head >
< title > Home </ title >
< script th :src= "@{sockjs.min.js}" ></ script >
< script th :src= "@{stomp.min.js}" ></ script >
< script th :src= "@{jquery.js}" ></ script >
</ head >
< body >
< p >
聊天室
</ p >

< form id= "handForm" >
< textarea rows= "4" cols= "60" name= "text" ></ textarea >
< input type= "submit" />
</ form >

< script th :inline= "javascript" >
$ ( '#handForm' ). submit ( function (e){
e. preventDefault ();
var text = $ ( '#handForm' ). find ( ' textarea [name="text"]' ). val ();
sendSpittle ( text );
});

var sock = new SockJS ( "/endpointChat" ); //连接endpoint名为endpointChat的endpoint
var stomp = Stomp . over ( sock );
stomp . connect ( 'guest' , 'guest' , function (frame) {
stomp . subscribe ( "/user/queue/notifications" , handleNotification ); //订阅/user/queue/notifications发送的消息,这里与在控制器的messagingTemplate.convertAndSendToUser中定义的订阅地址保持一致。
//此处多一个/user,且是必须的,使用/user之后才能发送消息到指定用户
});



function handleNotification (message) {
$ ( '#output' ). append ( "<b>Received: " + message. body + "</b><br/>" )
}

function sendSpittle (text) {
stomp . send ( "/chat" , {}, text); //3
}
$ ( '#stop' ). click ( function () { sock . close ()});
</ script >

< div id= "output" ></ div >
</ body >
</ html >
(7)增加页面的viewController;
package com.hand;

import org.springframework.context.annotation. Configuration ;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
* Created by lqy on 2017-11-28.
*/
@Configuration
public class WebmvcConfig extends WebMvcConfigurerAdapter{
@Override
public void addViewControllers(ViewControllerRegistry registry){
//registry.addViewController("/hand").setViewName("/Hand");
registry.addViewController( "/login" ).setViewName( "/login" );
registry.addViewController( "/chat" ).setViewName( "/chat" );
}
}
(8)运行
预期效果:两个用户登录系统,可以互发消息。一个浏览器用户会话session是共享的,在浏览器设置两个独立的用户,从而实现用户会话session隔离。http://localhos:8080/login

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Web Spring Boot 是一种用于快速开发 Web 应用程序的框架。它是基于 Spring 框架的轻量级解决方案,提供了强大的开发工具和功能,可帮助开发人员更高效地构建和部署网页应用。 Web Spring Boot 的实践过程通常包括以下几个步骤: 1. 配置项目环境:首先,我们需要配置项目的开发环境。我们可以使用 Maven 或 Gradle 来管理项目依赖,并选择适合的开发工具,如 Eclipse 或 IntelliJ IDEA。 2. 创建 Spring Boot 项目:创建一个基于 Spring BootWeb 项目。我们可以通过 Spring Initializr 网站或使用命令行工具来创建项目。 3. 定义数据模型:根据应用需求,我们可以使用 Java 类来定义数据模型。这些类通常称为实体类,我们可以使用注解来指定数据表、字段以及关系等。 4. 编写控制器:控制器是处理用户请求的核心部分。我们可以使用注解来标识请求 URL、请求类型以及方法,然后在方法中编写业务逻辑。控制器的返回值通常是一个视图,我们可以通过模板引擎将动态数据渲染到视图中。 5. 配置视图模板:视图模板是用于渲染动态内容的文件。Spring Boot 支持多种模板引擎,如 Thymeleaf、Freemarker 和 Velocity。我们可以选择适合自己的模板引擎,并进行相应的配置。 6. 完成业务逻辑:根据应用需求,我们可以编写其他的业务逻辑代码,如数据验证、数据处理、权限控制等。 7. 部署和测试:完成开发后,我们可以将项目部署到服务器上,并进行测试。可以使用内嵌的 Tomcat 容器来启动项目,并使用 Postman 或浏览器来发送请求,验证项目的功能和性能。 Web Spring Boot 的优势在于提供了快速开发的能力,减少了繁琐的配置工作,让开发人员更专注于业务逻辑的实现。它还提供了一系列的自动化工具和库,如自动配置、自动刷新等,大大提高了开发效率。此外,Spring Boot 还提供了丰富的扩展和集成能力,可以与其他框架和服务进行无缝集成,满足不同项目的需求。 总而言之,Web Spring Boot 是一种强大且高效的开发框架,适用于开发各种规模的 Web 应用程序。通过合理的配置和编写代码,开发人员可以快速构建出功能完善、高性能的网页应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值