首页 > 解决方案 > 弹簧靴 | 自动装配 CloseableHttpClient 失败 NoSuchBeanDefinitionException

问题描述

我知道这个问题是多余的并且被问了好几次,请多多包涵。我有点卡在这里,否则不会发布这个。我在HttpClientConfig下面有这个类,用@Configuration注解,我在这个类中定义了一个CloseableHttpClient bean,只用@Bean注解。


package demo.spice.test.config;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
@EnableScheduling
/**
 * This Class Uses a connection pool to re-use connections and save overhead of
 * creating connections. <strong>Supports both HTTP and HTTPS</strong>
 * <p>
 * Has a custom connection keep-alive strategy (to apply a default keep-alive if
 * one isn't specified). Starts an idle connection monitor to continuously clean
 * up stale connections.
 * </p>
 */
public class HttpClientConfig {
  private static final Logger logger = LoggerFactory.getLogger(HttpClientConfig.class);

  // Determines the timeout in milliseconds until a connection is established.
  private static final int CONNECT_TIMEOUT = 30000;
  // The timeout when requesting a connection from the connection manager.
  private static final int REQUEST_TIMEOUT = 30000;
  // The timeout for waiting for data
  private static final int SOCKET_TIMEOUT = 60000;
  private static final int MAX_TOTAL_CONNECTIONS = 50;
  private static final int DEFAULT_KEEP_ALIVE_TIME_MILLIS = 10 * 1000;
  private static final int CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS = 30;

  /**
   * This connection pool ensures that already opened connections are reused.
   * 
   * @return PoolingHttpClientConnectionManager
   */
  @Bean
  public PoolingHttpClientConnectionManager poolingConnectionManager() {
    logger.info("Inside HttpClientConfig. poolingConnectionManager() ");
    SSLContextBuilder builder = new SSLContextBuilder();
    try {
      builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
    } catch (NoSuchAlgorithmException | KeyStoreException e) {
      logger.error(
          "Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
    }

    SSLConnectionSocketFactory sslsf = null;
    try {
      sslsf = new SSLConnectionSocketFactory(builder.build());
    } catch (KeyManagementException | NoSuchAlgorithmException e) {
      logger.error(
          "Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e);
    }

    Registry<ConnectionSocketFactory> socketFactoryRegistry =
        RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslsf)
            .register("http", new PlainConnectionSocketFactory()).build();

    PoolingHttpClientConnectionManager poolingConnectionManager =
        new PoolingHttpClientConnectionManager(socketFactoryRegistry);
    poolingConnectionManager.setMaxTotal(MAX_TOTAL_CONNECTIONS);
    // Increase default max connection per route to 4
    poolingConnectionManager.setDefaultMaxPerRoute(4);
    logger.info("Exiting HttpClientConfig. poolingConnectionManager() ");
    return poolingConnectionManager;
  }

  /**
   * <p>
   * A connection Keep-Alive strategy determines how long a connection may remain unused in the pool
   * until it is closed. This ensures that connections that are no longer needed are closed again
   * promptly.
   * </p>
   * <p>
   * If the server does not send a Keep-Alive header in the response, the connections are kept alive
   * for 10 seconds by default. This implementation is a workaround to bypass the Apache Keep-Alive
   * strategy. Apaches strategy assumes that connections should remain alive indefinitely if the
   * server does not send a Keep-Alive
   * </p>
   * 
   * @return ConnectionKeepAliveStrategy
   */
  @Bean
  public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() {
    return new ConnectionKeepAliveStrategy() {
      @Override
      public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
        HeaderElementIterator it =
            new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        while (it.hasNext()) {
          HeaderElement he = it.nextElement();
          String param = he.getName();
          String value = he.getValue();

          if (value != null && param.equalsIgnoreCase("timeout")) {
            return Long.parseLong(value) * 1000;
          }
        }
        return DEFAULT_KEEP_ALIVE_TIME_MILLIS;
      }
    };
  }

  @Bean
  public CloseableHttpClient httpClient() {
    logger.info("Inside HttpClientConfig. httpClient() ");
    RequestConfig requestConfig =
        RequestConfig.custom().setConnectionRequestTimeout(REQUEST_TIMEOUT)
            .setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();

    logger.info("Exiting HttpClientConfig. httpClient() ");
    return HttpClients.custom().setDefaultRequestConfig(requestConfig)
        .setConnectionManager(poolingConnectionManager())
        .setKeepAliveStrategy(connectionKeepAliveStrategy()).build();
  }

  /**
   * Thread, which periodically check all connections and free up which have not been used and idle
   * time has elapsed runs every 20 seconds and closes Outdated connections as well as long waiting
   * connections
   * 
   * @param connectionManager
   * @return
   */
  @Bean
  public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager) {
    return new Runnable() {
      @Override
      @Scheduled(fixedDelay = 10000)
      public void run() {
        logger.info("Scheduler Running");
        try {
          if (connectionManager != null) {
            logger.info("run IdleConnectionMonitor - Closing expired and idle connections...");
            connectionManager.closeExpiredConnections();
            connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS,
                TimeUnit.SECONDS);
          } else {
            logger
                .info("run IdleConnectionMonitor - Http Client Connection manager is not initialised");
          }
        } catch (Exception e) {
          logger.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}",
              e.getMessage(), e);
        }
      }
    };
  }

  @Bean
  public TaskScheduler taskScheduler() {
    ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
    scheduler.setThreadNamePrefix("poolScheduler");
    scheduler.setPoolSize(50);
    return scheduler;
  }
}

我有另一个类,我在其中自动装配 CloseableHttpClient,但出现错误: 创建名称为“callHelper”的 bean 时出错:通过字段“closeableHttpClient”表示的不满足的依赖关系;嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException:没有“org.apache.http.impl.client.CloseableHttpClient”类型的合格 bean 可用:预计至少有 1 个有资格作为自动装配候选者的 bean。依赖注解

package org.spice.demo.service;

/**
imports here
**/

@Component
public class CallHelper {


  @Autowired
  private CloseableHttpClient closeableHttpClient;

  /**
   * @param httpRequest
   */
  private void executeCall(HttpRequestBase httpRequest) {
    logger.info(" executeCall: " + httpRequest.toString());
    for (Header header : httpRequest.getAllHeaders()) {
      logger.debug("Header : key = " + header.getName() + " value = " + header.getValue());
    }
    CloseableHttpResponse response = null;
    try {
      response = closeableHttpClient.execute(httpRequest);
      logger.debug(response.toString());
      logger.info(" Inside Try executeCall: Ended");
    } catch (Exception e) {
      logger.error("Error while making http call", e);
    } finally {
      try {
        if (response != null) {
          EntityUtils.consumeQuietly(response.getEntity());
          response.close();
        }
      } catch (Exception e) {
        logger.error(e.getMessage(), e);
      }
    }
  }

标签: javaspringspring-bootautowired

解决方案


默认情况下,Spring Boot 处理来自主类(使用 SpringBootApplication 注释)的同一包(及以下)的配置。只需将您的配置类从 demo.spice.test.config 移动到 org.spice.demo.service 包。


推荐阅读