Spring从2.x迁移至3.x后Swish支付服务创建失败求助
问题描述
搭建Swish支付微服务时遇到服务创建失败,报错信息如下:
(Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'buildRestTemplate' defined in class path resource [se/challengenow/payment/swish/SwishConfig.class]: Failed to instantiate [org.springframework.web.client.RestTemplate]: Factory method 'buildRestTemplate' threw exception with message: Failed to create Swish payment service).
项目已从Spring 2迁移至Spring 3,对应的Swish配置代码如下:
@Slf4j @Configuration public class SwishConfig { private final String keystorePassword; private final String keystoreName; public SwishConfig(@Value("${cn.keystore.password}") final String keystorePassword, @Value("${cn.keystore.name}") final String keystoreName) { this.keystorePassword = keystorePassword; this.keystoreName = keystoreName; } @Bean public RestTemplate buildRestTemplate() { final RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); final SSLContext sslContext; try { KeyStore keyStore = KeyStore.getInstance("PKCS12"); File file = ResourceUtils.getFile("classpath:secret/" + keystoreName); FileInputStream instream = new FileInputStream(file); try { keyStore.load(instream, keystorePassword.toCharArray()); } finally { instream.close(); } log.info("init keystore = {}", file.getAbsolutePath()); // Trust own CA and all self-signed certs sslContext = SSLContexts.custom() .loadKeyMaterial(keyStore, keystorePassword.toCharArray()) .build(); final HttpClient client = (HttpClient) HttpClients.custom() .setSSLContext(sslContext) .build(); /*final HttpClient client = HttpClients.custom() .setSSLContext(sslContext) .build();*/ final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client); final RestTemplate restTemplate = restTemplateBuilder .errorHandler(new ResponseErrorHandler() { @Override public boolean hasError(final ClientHttpResponse clientHttpResponse) throws IOException { return !clientHttpResponse.getStatusCode().is2xxSuccessful(); } @Override public void handleError(final ClientHttpResponse clientHttpResponse) throws IOException { final String result = new BufferedReader(new InputStreamReader(clientHttpResponse.getBody())) .lines().collect(Collectors.joining("\n")); log.error("Error from swish: {}", result); final String errorCodes = SwishPaymentService.getErrorCodes(result).orElse(result); throw new RuntimeException("For possible error codes (" + errorCodes + ")"); } }) .interceptors(new LoggingRequestInterceptor()) .build(); restTemplate.setRequestFactory(requestFactory); return restTemplate; } catch (final Exception e) { throw new RuntimeException("Failed to create Swish payment service", e); } } }
排查分析与修复建议
结合Spring 3(尤其是Spring Boot 3)的版本特性,以下是可能导致服务创建失败的原因及修复方案:
资源读取方式不兼容
Spring 3中ResourceUtils.getFile()已被标记为过时,且当资源打包在Jar包内时,无法直接获取磁盘File对象,会抛出FileNotFoundException。
修复方案:使用Spring的ResourceLoader加载资源,通过输入流读取密钥库:// 注入ResourceLoader private final ResourceLoader resourceLoader; public SwishConfig(@Value("${cn.keystore.password}") final String keystorePassword, @Value("${cn.keystore.name}") final String keystoreName, ResourceLoader resourceLoader) { this.keystorePassword = keystorePassword; this.keystoreName = keystoreName; this.resourceLoader = resourceLoader; } // 替换资源读取逻辑 Resource resource = resourceLoader.getResource("classpath:secret/" + keystoreName); try (InputStream instream = resource.getInputStream()) { keyStore.load(instream, keystorePassword.toCharArray()); }Apache HttpClient版本不兼容
Spring Boot 3默认使用Apache HttpClient 5.x,而Spring Boot 2用的是4.x版本,两者API存在差异:5.x中HttpClients.custom().build()返回CloseableHttpClient,强转为旧版HttpClient会触发ClassCastException。
修复方案:修正HttpClient的类型声明:final CloseableHttpClient client = HttpClients.custom() .setSSLContext(sslContext) .build(); final ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(client);RestTemplateBuilder配置优化
原代码先构建RestTemplate再手动设置requestFactory,不符合Spring 3的配置规范,可简化为直接通过builder配置:final RestTemplate restTemplate = restTemplateBuilder .errorHandler(...) .interceptors(new LoggingRequestInterceptor()) .requestFactory(() -> requestFactory) .build();异常信息缺失
原代码仅抛出通用异常信息,建议添加日志打印完整异常堆栈,方便定位问题:catch (final Exception e) { log.error("Failed to create Swish payment service", e); throw new RuntimeException("Failed to create Swish payment service", e); }
内容的提问来源于stack exchange,提问作者Arif ullahkhan
相关产品推荐
相关产品推荐

