Spring WebSocket Portfolio前端实现:AngularJS与WebSocket的实时交互终极指南
想要构建实时股票交易系统吗?Spring WebSocket Portfolio项目展示了如何利用AngularJS前端与Spring WebSocket后端实现高效的实时数据交互。这个完整的示例项目为您提供了构建现代实时Web应用的最佳实践。通过STOMP协议和SockJS库,前端能够与服务器建立双向通信,实现股票价格的实时更新和交易操作的即时反馈。
📊 项目概述与核心架构
Spring WebSocket Portfolio是一个完整的股票交易模拟系统,展示了Spring Framework在构建WebSocket应用方面的强大能力。前端使用AngularJS框架,通过STOMP over WebSocket与后端进行实时通信。
项目的主要功能包括:
- 实时股票价格更新
- 股票买卖交易功能
- 用户持仓管理
- 交易通知和错误处理
🚀 快速启动项目
要开始使用这个项目,您需要先克隆仓库:
git clone https://gitcode.com/gh_mirrors/sp/spring-websocket-portfolio
cd spring-websocket-portfolio
项目支持多种服务器容器,包括Tomcat、Jetty、WildFly和GlassFish。最简单的方式是使用Tomcat:
- 设置环境变量
TOMCAT_HOME - 运行部署脚本:
./deployTomcat.sh - 在浏览器中访问:http://localhost:8080/spring-websocket-portfolio/index.html
🔧 前端架构设计
AngularJS模块结构
前端代码位于 src/main/webapp/ 目录下,主要包含以下关键文件:
- index.html - 主页面布局和模板
- app.js - AngularJS应用主模块定义
- controllers.js - 控制器逻辑
- services.js - WebSocket服务封装
核心模块定义
在 app.js 中,项目定义了AngularJS应用的主模块:
var springPortfolio = angular
.module('springPortfolio', ['springPortfolio.controllers', 'springPortfolio.services']);
🌐 WebSocket连接管理
STOMP客户端封装
项目在 services.js 中创建了一个 StompClient 工厂,封装了WebSocket连接的核心功能:
.factory('StompClient', ['sockJsProtocols', '$q', function (sockJsProtocols, $q) {
var stompClient;
var wrappedSocket = {
init: function (url) {
if (sockJsProtocols.length > 0) {
stompClient = webstomp.over(new SockJS(url, null, {transports: sockJsProtocols}));
}
else {
stompClient = webstomp.over(new SockJS(url));
}
},
connect: function () {
return $q(function (resolve, reject) {
if (!stompClient) {
reject("STOMP client not created");
} else {
stompClient.connect({}, function (frame) {
resolve(frame);
}, function (error) {
reject("STOMP protocol error " + error);
});
}
});
}
};
return wrappedSocket;
}])
交易服务实现
TradeService 工厂提供了与后端通信的高级API:
.factory('TradeService', ['StompClient', '$q', function (stompClient, $q) {
return {
connect: function (url) {
stompClient.init(url);
return stompClient.connect().then(function (frame) {
return frame.headers['user-name'];
});
},
loadPositions: function() {
return stompClient.subscribeSingle("/app/positions");
},
fetchQuoteStream: function () {
return stompClient.subscribe("/topic/price.stock.*");
}
};
}])
📈 实时数据流处理
股票价格实时更新
在 controllers.js 中,PortfolioController 控制器处理实时数据流:
var processQuote = function(quote) {
var existing = $scope.positions[quote.ticker];
if(existing) {
existing.change = quote.price - existing.price;
existing.price = quote.price;
}
};
tradeService.fetchQuoteStream().then(null, null,
function(quote) {
processQuote(quote);
}
);
持仓更新处理
var udpatePosition = function(position) {
var existing = $scope.positions[position.ticker];
if(existing) {
existing.shares = position.shares;
}
};
tradeService.fetchPositionUpdateStream().then(null, null,
function(position) {
udpatePosition(position);
}
);
💼 交易功能实现
交易模态框控制器
项目使用AngularUI Bootstrap实现交易模态框:
.controller('TradeModalController',
["$scope", "$uibModalInstance", "TradeService", "action", "position",
function ($scope, $uibModalInstance, tradeService, action, position) {
$scope.action = action;
$scope.position = position;
$scope.numberOfShares = 0;
$scope.trade = function () {
$uibModalInstance.close({action: action, position: position, numberOfShares: $scope.numberOfShares});
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
}])
交易验证逻辑
var validateTrade = function(trade) {
if (isNaN(trade.shares) || (trade.shares < 1)) {
$scope.notifications.push("Trade Error: Invalid number of shares");
return false;
}
if ((trade.action === "Sell") && (trade.shares > $scope.positions[trade.ticker].shares)) {
$scope.notifications.push("Trade Error: Not enough shares");
return false;
}
return true;
}
🎨 用户界面设计
股票表格展示
在 index.html 中,使用AngularJS的 ng-repeat 指令动态展示股票持仓:
<tr ng-repeat="(ticker, position) in positions">
<td>{{position.company}}</td>
<td>{{position.ticker}}</td>
<td class="number">{{position.price | currency:"$"}}</td>
<td class="number">
<span ng-show="position.change > 0" class="glyphicon glyphicon-arrow-up"></span>
<span ng-show="position.change == 0" class="glyphicon glyphicon-arrow-right"></span>
<span ng-show="position.change < 0" class="glyphicon glyphicon-arrow-down"></span>
</td>
<td class="number">{{position.change | percent:position.price}}</td>
<td class="number">{{position.shares | number}}</td>
<td class="number">{{position.price * position.shares | currency:"$"}}</td>
<td class="trade-buttons">
<button class="btn btn-primary" ng-click="openTradeModal('buy', position)">Buy</button>
<button class="btn btn-primary" ng-click="openTradeModal('sell', position)">Sell</button>
</td>
</tr>
自定义过滤器
项目实现了多个自定义过滤器来格式化数据:
.filter('percent', ['$filter', function ($filter) {
return function (input, total) {
return $filter('number')(input / total * 100, 1) + '%';
};
}])
.filter('totalPortfolioShares', [function () {
return function (positions) {
var total = 0;
for(var ticker in positions) {
total += positions[ticker].shares;
}
return total;
};
}])
🔌 WebSocket连接配置
SockJS回退策略
项目支持多种传输协议,确保在不同浏览器环境下的兼容性:
.constant('sockJsProtocols', []) // 空数组表示使用所有可用协议
订阅模式
前端通过不同的目的地订阅不同类型的消息:
/app/positions- 获取初始持仓数据/topic/price.stock.*- 订阅股票价格更新/user/queue/position-updates- 接收持仓更新/user/queue/errors- 接收错误信息
🛠️ 错误处理与通知
错误流订阅
tradeService.fetchErrorStream().then(null, null,
function (error) {
pushNotification(error);
}
);
通知推送机制
var pushNotification = function(message) {
$scope.notifications.unshift(message);
};
📱 响应式设计
项目使用Bootstrap和Flat UI实现响应式设计,确保在不同设备上都有良好的用户体验。CSS样式文件位于 static/css/ 目录下:
- portfolio.css - 主样式表
- login.css - 登录页面样式
🚨 最佳实践总结
1. 连接管理
- 使用工厂模式封装WebSocket连接
- 支持SockJS回退机制
- 提供统一的错误处理
2. 数据流处理
- 使用Promise处理异步操作
- 分离数据获取和界面更新逻辑
- 实现实时数据订阅机制
3. 用户界面
- 使用AngularJS双向数据绑定
- 实现模块化的控制器设计
- 提供友好的用户反馈
4. 错误处理
- 统一的错误通知机制
- 交易验证逻辑
- 连接状态管理
🎯 项目优势
- 完整的实时交互示例 - 展示了从连接到数据处理的全过程
- 生产级代码质量 - 遵循AngularJS最佳实践
- 多种服务器支持 - 兼容主流Java Web容器
- 完善的错误处理 - 提供完整的异常处理机制
- 响应式设计 - 适配不同屏幕尺寸
通过这个项目,您可以学习到如何构建一个完整的实时Web应用前端,掌握AngularJS与WebSocket的集成技巧,为您的下一个实时应用项目打下坚实基础。
📚 深入学习资源
- 查看完整的控制器实现:controllers.js
- 学习服务层设计:services.js
- 研究HTML模板:index.html
这个Spring WebSocket Portfolio项目为您提供了构建现代实时Web应用的完整前端解决方案,无论是学习还是实际项目开发,都是一个极佳的参考示例。🚀
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



