本文将指导你如何使用Spring Boot框架快速对接StockTV的印度股票数据API,包含REST API调用、WebSocket实时推送以及企业级应用开发的最佳实践。
一、环境准备
1. 创建Spring Boot项目
使用Spring Initializr创建项目(start.spring.io/),选择以下依赖:
- Spring Web
- Spring WebSocket
- Lombok
- Apache HttpClient
2. 配置依赖(pom.xml)
xml
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- 工具库 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
</dependencies>
3. 配置API密钥
在application.yml
中添加配置:
yml
stocktv:
api:
base-url: https://api.stocktv.top
key: YOUR_API_KEY # 联系官方获取
ws-url: wss://ws-api.stocktv.top/connect
二、核心功能实现
1. 配置HTTP客户端
java
@Configuration
public class HttpClientConfig {
@Bean
public CloseableHttpClient httpClient() {
return HttpClients.custom()
.setConnectionManager(new PoolingHttpClientConnectionManager())
.build();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2. 封装API服务
java
@Service
@RequiredArgsConstructor
public class StockApiService {
private final CloseableHttpClient httpClient;
@Value("${stocktv.api.base-url}")
private String baseUrl;
@Value("${stocktv.api.key}")
private String apiKey;
// 获取印度股票列表
public List<Stock> getIndianStocks(int pageSize, int page) throws IOException {
String url = String.format("%s/stock/stocks?countryId=14&exchangeId=46&pageSize=%d&page=%d&key=%s",
baseUrl, pageSize, page, apiKey);
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = httpClient.execute(request)) {
String json = EntityUtils.toString(response.getEntity());
return parseStockList(json);
}
}
// 解析股票数据
private List<Stock> parseStockList(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
JsonNode records = root.path("data").path("records");
List<Stock> stocks = new ArrayList<>();
for (JsonNode node : records) {
Stock stock = Stock.builder()
.id(node.path("id").asInt())
.symbol(node.path("symbol").asText())
.name(node.path("name").asText())
.lastPrice(node.path("last").asDouble())
.changePercent(node.path("chgPct").asDouble())
.volume(node.path("volume").asLong())
.build();
stocks.add(stock);
}
return stocks;
}
// 其他API方法...
}
// Lombok数据模型
@Data
@Builder
public class Stock {
private int id;
private String symbol;
private String name;
private double lastPrice;
private double changePercent;
private long volume;
}
3. 实现K线数据服务
java
@Service
public class KlineService {
private final RestTemplate restTemplate;
@Value("${stocktv.api.base-url}")
private String baseUrl;
@Value("${stocktv.api.key}")
private String apiKey;
// 获取K线数据
public List<Kline> getKlines(int pid, String interval) {
String url = String.format("%s/stock/kline?pid=%d&interval=%s&key=%s",
baseUrl, pid, interval, apiKey);
ResponseEntity<List<Kline>> response = restTemplate.exchange(
url,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Kline>>() {}
);
return response.getBody();
}
// K线数据模型
@Data
public static class Kline {
private long time;
private double open;
private double high;
private double low;
private double close;
private long volume;
}
}
三、WebSocket实时推送
1. WebSocket配置
java
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
2. WebSocket服务端
java
@Service
public class StockWebSocketService {
@Value("${stocktv.api.ws-url}")
private String wsUrl;
@Value("${stocktv.api.key}")
private String apiKey;
private WebSocketStompClient stompClient;
@PostConstruct
public void init() {
// 初始化WebSocket客户端
WebSocketClient client = new StandardWebSocketClient();
this.stompClient = new WebSocketStompClient(client);
this.stompClient.setMessageConverter(new MappingJackson2MessageConverter());
connectToStockTv();
}
private void connectToStockTv() {
String url = wsUrl + "?key=" + apiKey;
StompSessionHandler handler = new StockSessionHandler();
this.stompClient.connect(url, handler);
}
// 自定义Session处理器
private class StockSessionHandler extends StompSessionHandlerAdapter {
@Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
// 订阅所有股票更新
session.subscribe("/topic/stocks", new StockFrameHandler());
// 发送订阅请求
Map<String, Object> subscription = Map.of(
"action", "subscribe",
"pids", List.of(7310, 41602) // 订阅的股票ID
);
session.send("/app/subscribe", subscription);
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
// 处理实时数据
System.out.println("Received: " + payload);
}
}
// 自定义消息处理器
private class StockFrameHandler implements StompFrameHandler {
@Override
public Type getPayloadType(StompHeaders headers) {
return Map.class;
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
Map<String, Object> data = (Map<String, Object>) payload;
System.out.println("Real-time update: " + data);
}
}
}
3. WebSocket控制器
java
@Controller
public class StockWebSocketController {
private final SimpMessagingTemplate messagingTemplate;
public StockWebSocketController(SimpMessagingTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
// 广播股票更新
public void broadcastStockUpdate(Stock stock) {
messagingTemplate.convertAndSend("/topic/stocks", stock);
}
}
四、REST API接口
1. 股票控制器
java
@RestController
@RequestMapping("/api/stocks")
@RequiredArgsConstructor
public class StockController {
private final StockApiService stockApiService;
private final KlineService klineService;
// 获取印度股票列表
@GetMapping("/india")
public ResponseEntity<List<Stock>> getIndianStocks(
@RequestParam(defaultValue = "100") int pageSize,
@RequestParam(defaultValue = "1") int page) {
try {
return ResponseEntity.ok(stockApiService.getIndianStocks(pageSize, page));
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
// 获取股票K线
@GetMapping("/{id}/kline")
public ResponseEntity<List<Kline>> getKlineData(
@PathVariable int id,
@RequestParam(defaultValue = "PT15M") String interval) {
return ResponseEntity.ok(klineService.getKlines(id, interval));
}
}
五、企业级增强功能
1. 缓存配置
java
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("stocks", "kline");
}
}
2. 限流与熔断
java
@Service
public class StockService {
@RateLimiter(name = "stockApiRateLimit", fallbackMethod = "fallback")
@CircuitBreaker(name = "stockApi", fallbackMethod = "fallback")
@Retry(name = "stockApiRetry")
public List<Stock> getStocksWithResilience() {
// 调用API
}
public List<Stock> fallback(Throwable t) {
// 返回缓存数据或默认值
return Collections.emptyList();
}
}
3. 定时任务
java
@Service
public class StockDataSyncService {
private final StockApiService stockApiService;
private final StockWebSocketController webSocketController;
@Scheduled(fixedRate = 60000) // 每分钟同步一次
public void syncStockData() {
List<Stock> stocks = stockApiService.getIndianStocks(100, 1);
// 处理并存储数据
stocks.forEach(webSocketController::broadcastStockUpdate);
}
}
六、安全配置
1. API密钥保护
java
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and()
.httpBasic();
}
}
2. 敏感配置加密
java
@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource("application.yml"));
configurer.setIgnoreUnresolvablePlaceholders(true);
// 使用Jasypt加密
EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
config.setPasswordEnvName("ENCRYPTION_PASSWORD");
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setConfig(config);
configurer.setPropertySources(
new EncryptablePropertiesPropertySource(
"encryptedProps",
encryptor,
new PropertiesPropertySource("appProps", loadProperties())
)
);
return configurer;
}
七、部署与监控
1. Dockerfile
dockerfile
FROM openjdk:17-jdk-slim
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
2. Prometheus监控配置
yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
八、项目结构
bash
src/
├── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ ├── config/ # 配置类
│ │ ├── controller/ # REST控制器
│ │ ├── service/ # 业务服务
│ │ ├── model/ # 数据模型
│ │ ├── websocket/ # WebSocket相关
│ │ └── Application.java # 启动类
│ └── resources/
│ ├── application.yml # 配置文件
│ └── static/ # 静态资源
└── test/ # 测试代码
九、总结
通过本文我们实现了:
- Spring Boot基础配置与依赖管理
- 印度股票数据的REST API封装
- WebSocket实时数据推送
- 企业级功能增强(缓存、限流、熔断)
- 安全配置与生产部署方案
关键优势:
- 模块化设计:清晰的分层架构
- 实时性:WebSocket提供毫秒级数据更新
- 弹性架构:Resilience4j保障系统稳定性
- 生产就绪:包含监控、安全、容器化部署方案
完整示例代码:[GitHub仓库链接] 官方API文档:[stocktv.top]
通过此方案,企业可快速构建高可用、实时的印度股票数据服务,满足量化交易、行情展示等多种业务场景需求。