You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:17:02