使用Spring Boot对接印度股票市场API开发实践
本文将指导你如何使用Spring Boot框架快速对接StockTV的印度股票数据API,包含REST API调用、WebSocket实时推送以及企业级应用开发的最佳实践。
一、环境准备
1. 创建Spring Boot项目
使用Spring Initializr创建项目(https://start.spring.io/),选择以下依赖:
- Spring Web
- Spring WebSocket
- Lombok
- Apache HttpClient
2. 配置依赖(pom.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
中添加配置:
stocktv:api:base-url: https://api.stocktv.topkey: YOUR_API_KEY # 联系官方获取ws-url: wss://ws-api.stocktv.top/connect
二、核心功能实现
1. 配置HTTP客户端
@Configuration
public class HttpClientConfig {@Beanpublic CloseableHttpClient httpClient() {return HttpClients.custom().setConnectionManager(new PoolingHttpClientConnectionManager()).build();}@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}
2. 封装API服务
@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线数据服务
@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线数据模型@Datapublic static class Kline {private long time;private double open;private double high;private double low;private double close;private long volume;}
}
三、WebSocket实时推送
1. WebSocket配置
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/topic");config.setApplicationDestinationPrefixes("/app");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/ws").withSockJS();}
}
2. WebSocket服务端
@Service
public class StockWebSocketService {@Value("${stocktv.api.ws-url}")private String wsUrl;@Value("${stocktv.api.key}")private String apiKey;private WebSocketStompClient stompClient;@PostConstructpublic 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 {@Overridepublic 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);}@Overridepublic void handleFrame(StompHeaders headers, Object payload) {// 处理实时数据System.out.println("Received: " + payload);}}// 自定义消息处理器private class StockFrameHandler implements StompFrameHandler {@Overridepublic Type getPayloadType(StompHeaders headers) {return Map.class;}@Overridepublic void handleFrame(StompHeaders headers, Object payload) {Map<String, Object> data = (Map<String, Object>) payload;System.out.println("Real-time update: " + data);}}
}
3. WebSocket控制器
@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. 股票控制器
@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. 缓存配置
@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic CacheManager cacheManager() {return new ConcurrentMapCacheManager("stocks", "kline");}
}
2. 限流与熔断
@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. 定时任务
@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密钥保护
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/api/**").authenticated().and().httpBasic();}
}
2. 敏感配置加密
@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
FROM openjdk:17-jdk-slim
VOLUME /tmp
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
2. Prometheus监控配置
management:endpoints:web:exposure:include: health,info,metrics,prometheusmetrics:export:prometheus:enabled: true
八、项目结构
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文档:[https://stocktv.top]
通过此方案,企业可快速构建高可用、实时的印度股票数据服务,满足量化交易、行情展示等多种业务场景需求。