SpringBoot 中动态的对Bean对象设置和获取

本文详细介绍了如何在Spring Boot中通过ApplicationContextAware获取静态bean和动态设置bean的过程,包括使用BootBeanGetter获取bean及BootBeanSetter动态注册bean的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好记忆不如烂笔头,能记下点东西,就记下点,有时间拿出来看看,也会发觉不一样的感受

目录

前言

bean对象的获取

bean对象的设置


前言


现实中,总会有将bean获取或者动态(程序启动时候,通过参数判断是否需要创建bean对象,并放入到applicationcontext中去)将bean放入到springboot全局中去。

bean对象的获取

bean 对象的获取很容易,只需要在ApplicationContext 中拿取就是了,需要 实现的接口是:ApplicationContextAware

代码如下:
 

@Component
public class BootBeanGetter implements ApplicationContextAware {
	private static ApplicationContext applicationContext;
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		setApplicationContextStatic(applicationContext);
	}
	
	public static <T> T getClassBean(Class<T> requiredType) {
		return applicationContext.getBean(requiredType);
	}
	
	public static Object getBean(String name){
		return applicationContext.getBean(name);
	}
	
	public static ApplicationContext getApplicationContext() {
		return applicationContext;
	}
	
	public static void setApplicationContextStatic(final ApplicationContext applicationContext) {
		BootBeanGetter.applicationContext = applicationContext;
	}

}

如此就可以在代码中,使用 BootBeanGetter 来获取bean对象了。

bean对象的设置

那么,如何动态设置bean对象呐,此次的bean对象的动态设置,是指在实现了 CommandLineRunner 等接口情况下,通过动态获取配置,进而动态将获取的信息转换得到的bean(根据条件,可能不存在,即无法创建bean)放入到ApplicationContext 中去,操作如下:

@Component
public class BootBeanSetter implements ApplicationContextAware {
	
	/**
	 * 配置bean实例.
	 */
	private static ConfigurableApplicationContext applicationContext;
	
	public BootBeanSetter(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}
	
	/**
	 * 动态将bean放入
	 * @param name
	 * @param clazz
	 * @param args
	 */
	public static void registerBean(String name, Class clazz, Object... args) {
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
		if (args.length > 0) {
			for (Object arg : args) {
				beanDefinitionBuilder.addConstructorArgValue(arg);
			}
		}
		BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
		BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
		//注册bean对象.
		beanFactory.registerBeanDefinition(name, beanDefinition);
	}
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		BootBeanSetter.applicationContext = (ConfigurableApplicationContext) applicationContext;
	}
}

如此操作,可以将动态创建的bean放入到springboot的ApplicationContext 对象中去。

 

 

<think>我们面对的需求是:在SpringBoot中配置一个HttpClientBean,使得不同的HTTP请求可以设置不同的超时时间。根据之前的讨论,我们知道ApacheHttpClient可以通过RequestConfig为每个请求单独设置超时参数。因此,我们需要在SpringBoot中创建一个可配置的HttpClientBean,并支持在每次请求时设置不同的RequestConfig。步骤:1.创建HttpClient连接池(推荐使用PoolingHttpClientConnectionManager)2.配置一个可自定义RequestConfig的HttpClientBean3.在发送请求时,为每个请求设置不同的RequestConfig(通过HttpGet或HttpPost的setConfig方法)但是,在SpringBoot中,我们通常使用RestTemplate来发送HTTP请求。然而,RestTemplate默认使用的是SimpleClientHttpRequestFactory(基于JDK的HttpURLConnection),它不支持每个请求单独设置超时(只能全局设置)。因此,我们需要使用HttpComponentsClientHttpRequestFactory(基于ApacheHttpClient)来支持更细粒度的控制。所以,我们将:-配置一个基于ApacheHttpClient的RestTemplate-在每次请求时,通过HttpComponentsClientHttpRequestFactory的HttpClient来执行请求,并为每个请求设置不同的RequestConfig然而,RestTemplate本身并不直接支持为每个请求设置不同的超时时间。因此,我们需要在每次请求前动态设置RequestConfig。解决方案:1.创建可设置连接池的HttpClientBean2.创建HttpComponentsClientHttpRequestFactory,并设置HttpClient3.创建RestTemplate,并设置HttpComponentsClientHttpRequestFactory4.在每次发送请求前,为HttpClient设置RequestConfig(但注意:HttpClient是线程安全的,但RequestConfig是在请求对象设置的,而不是在HttpClient上)因此,我们可以这样设计:每次构造请求(如HttpGet、HttpPost)时,为其设置RequestConfig。这样,我们就可以通过RestTemplate的拦截器(ClientHttpRequestInterceptor)来动态设置超时。但是,RestTemplate的拦截器并不直接操作底层的ApacheHttpClient请求对象。因此,我们需要更灵活的方式。另一种思路:不使用RestTemplate,而是直接使用我们配置的HttpClientBean来发送请求。这样我们就可以完全控制每个请求的配置。考虑到用户可能更倾向于使用RestTemplate,我们可以提供两种方案:方案一:直接使用ApacheHttpClient(推荐,因为更灵活)-配置一个HttpClientBean-在业务代码中,使用该HttpClient执行请求,并在每个请求对象设置RequestConfig方案二:扩展RestTemplate(通过自定义拦截器来设置超时,但相对复杂)这里我们采用方案一,因为更直接且符合需求。具体步骤:1.在SpringBoot配置类中创建HttpClientBean(使用连接池)2.在业务代码中注入HttpClient,然后创建HttpRequest(如HttpGet,HttpPost)并设置RequestConfig3.执行请求示例代码:配置类:```javaimportorg.apache.http.impl.client.CloseableHttpClient;importorg.apache.http.impl.client.HttpClients;importorg.apache.http.impl.conn.PoolingHttpClientConnectionManager;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassHttpClientConfig{@BeanpublicCloseableHttpClienthttpClient(){//创建连接池管理器PoolingHttpClientConnectionManagerconnectionManager=newPoolingHttpClientConnectionManager();connectionManager.setMaxTotal(200);//最大连接数connectionManager.setDefaultMaxPerRoute(50);//每个路由的最大连接数//创建HttpClientreturnHttpClients.custom().setConnectionManager(connectionManager).build();}}```在业务代码中使用:```javaimportorg.apache.http.client.config.RequestConfig;importorg.apache.http.client.methods.CloseableHttpResponse;importorg.apache.http.client.methods.HttpGet;importorg.apache.http.impl.client.CloseableHttpClient;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjava.io.IOException;@ServicepublicclassHttpService{@AutowiredprivateCloseableHttpClienthttpClient;publicvoidexecuteRequestWithDifferentTimeouts()throwsIOException{//请求1:短超时RequestConfigshortTimeoutConfig=RequestConfig.custom().setConnectTimeout(1000).setSocketTimeout(1000).build();HttpGetrequest1=newHttpGet("http://example.com/api1");request1.setConfig(shortTimeoutConfig);//请求2:长超时RequestConfiglongTimeoutConfig=RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(5000).build();HttpGetrequest2=newHttpGet("http://example.com/api2");request2.setConfig(longTimeoutConfig);//执行请求try(CloseableHttpResponseresponse1=httpClient.execute(request1);CloseableHttpResponseresponse2=httpClient.execute(request2)){//处理响应...}}}```但是,上述代码需要手动关闭响应,可以使用try-with-resources确保关闭。如果用户希望使用RestTemplate,我们可以通过自定义拦截器来动态设置超时。但是RestTemplate底层使用的是HttpClient,我们可以通过ThreadLocal传递RequestConfig,然后在创建请求时设置。方案二(使用RestTemplate)的复杂实现:1.自定义一个RequestConfig的持有类(使用ThreadLocal)2.自定义拦截器,在拦截器中获取ThreadLocal中的RequestConfig,并设置到HttpClient的请求中3.在发送请求前,将当前请求的RequestConfig设置到ThreadLocal中但是,这种方法需要注意线程安全问题(比如异步请求)ThreadLocal的清理。考虑到复杂性易错性,在需要为不同请求设置不同超时的场景下,直接使用HttpClient是更简单直接的方式。因此,我们推荐使用方案一:直接配置并使用CloseableHttpClient。另外,如果需要发送HTTPS请求,可以配置SSLContext(引用自[^3]中有发送HTTPS请求的示例,但这里我们主要关注超时配置)。总结:在SpringBoot中配置HttpClientBean,然后在每次请求时创建对应的RequestConfig并设置到请求对象上,即可实现不同请求不同超时时间。补充:关于资源释放使用CloseableHttpClient执行请求后,必须确保关闭响应(使用try-with-resources或者finally块),否则会导致连接泄漏。如果用户坚持使用RestTemplate,我们可以提供另一种方案:为每个不同的超时配置创建不同的RestTemplate实例。但这样会导致连接池不共享,资源使用效率低,不推荐。因此,我们最终选择直接使用CloseableHttpClient。引用说明:[^1]:在SpringBoot中配置RestTemplate的超时时间,但这是全局设置,无法满足不同请求不同超时的需求。[^2]:同样展示了RestTemplate的全局超时设置。[^3]:展示了SpringBoot整合HttpClient发送HTTPS请求的测试,我们可以参考其配置HttpClient连接池。所以,我们的解决方案是配置一个连接池的HttpClientBean,然后在每次请求时设置RequestConfig。代码实现:配置类(增加一些可配置参数,比如从配置文件读取):```javaimportorg.apache.http.client.config.RequestConfig;importorg.apache.http.impl.client.CloseableHttpClient;importorg.apache.http.impl.client.HttpClients;importorg.apache.http.impl.conn.PoolingHttpClientConnectionManager;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassHttpClientConfig{@Value("${http.client.max-total:200}")privateintmaxTotal;@Value("${http.client.default-max-per-route:50}")privateintdefaultMaxPerRoute;@BeanpublicPoolingHttpClientConnectionManagerpoolingHttpClientConnectionManager(){PoolingHttpClientConnectionManagermanager=newPoolingHttpClientConnectionManager();manager.setMaxTotal(maxTotal);manager.setDefaultMaxPerRoute(defaultMaxPerRoute);returnmanager;}@BeanpublicCloseableHttpClienthttpClient(PoolingHttpClientConnectionManagerpoolingHttpClientConnectionManager){//全局默认请求配置(可选)RequestConfigdefaultRequestConfig=RequestConfig.custom().setConnectTimeout(5000)//默认连接超时5秒.setSocketTimeout(5000)//默认读取超时5秒.build();returnHttpClients.custom().setConnectionManager(poolingHttpClientConnectionManager).setDefaultRequestConfig(defaultRequestConfig)//设置默认请求配置.build();}}```然后,在业务代码中,我们可以覆盖默认的RequestConfig。注意:我们为每个请求设置的RequestConfig会覆盖默认配置。这样,我们就实现了在SpringBoot中配置HttpClientBean,并支持不同请求设置不同超时时间。相关问题:1.如何在SpringBoot中配置HttpClient的连接池参数?->如上所示,通过PoolingHttpClientConnectionManager设置2.如何监控HttpClient请求的成功率超时率?->可以通过配置HttpRequestInterceptor来记录请求指标3.HttpClientOKHttp在超时处理机制上有何区别?->两者都可以设置超时,但API实现方式不同4.分布式系统中如何动态调整不同服务的HTTP超时时间?->可以通过配置中心动态获取超时配置5.如何处理HTTP请求超时后的重试熔断机制?->结合重试机制熔断框架(如Resilience4j)根据用户的问题,我们只详细回答了如何配置不同请求的超时时间。生成的相关问题:1.如何在SpringBoot中配置HttpClient的连接池参数?2.如何监控HttpClient请求的成功率超时率?3.HttpClientOKHttp在超时处理机制上有何区别?4.分布式系统中如何动态调整不同服务的HTTP超时时间?5.如何处理HTTP请求超时后的重试熔断机制?</think>在 Spring Boot 中为不同 HTTP 请求配置独立超时时间,可以通过自定义 `HttpClient` Bean 结合 `RequestConfig` 实现。以下是完整的解决方案: ### 配置支持动态超时的 HttpClient Bean ```java import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class HttpClientConfig { // 配置连接池 @Bean public PoolingHttpClientConnectionManager connectionManager() { PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(); manager.setMaxTotal(100); // 最大连接数 manager.setDefaultMaxPerRoute(20); // 每路由最大连接数 return manager; } // 创建支持动态超时的HttpClient @Bean public CloseableHttpClient httpClient(PoolingHttpClientConnectionManager manager) { return HttpClients.custom() .setConnectionManager(manager) .build(); // 不设置默认超时,由每个请求单独指定 } } ``` ### 使用不同超时时间的请求示例 ```java import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class HttpService { @Autowired private CloseableHttpClient httpClient; // 快速请求(短超时) public void fastRequest() throws Exception { RequestConfig config = RequestConfig.custom() .setConnectTimeout(1000) // 1秒连接超时 .setSocketTimeout(2000) // 2秒数据传输超时 .build(); HttpGet request = new HttpGet("https://api.example.com/health"); request.setConfig(config); try (CloseableHttpResponse response = httpClient.execute(request)) { // 处理响应 } } // 慢速请求(长超时) public void slowRequest() throws Exception { RequestConfig config = RequestConfig.custom() .setConnectTimeout(5000) // 5秒连接超时 .setSocketTimeout(10000) // 10秒数据传输超时 .build(); HttpPost request = new HttpPost("https://api.example.com/data-export"); request.setConfig(config); try (CloseableHttpResponse response = httpClient.execute(request)) { // 处理响应 } } } ``` ### 关键配置说明 | 配置方法 | 作用 | 推荐值范围 | |---------|------|-----------| | `setConnectTimeout()` | TCP连接建立超时 | 1000-5000ms | | `setSocketTimeout()` | 两次数据包间隔超时 | 3000-30000ms | | `setConnectionRequestTimeout()` | 从连接池获取连接超时 | 500-2000ms | ### 高级技巧:封装请求工具类 ```java public class HttpClientHelper { private final CloseableHttpClient client; public HttpClientHelper(CloseableHttpClient client) { this.client = client; } public String executeWithTimeout(String url, int connectTimeout, int socketTimeout) { RequestConfig config = RequestConfig.custom() .setConnectTimeout(connectTimeout) .setSocketTimeout(socketTimeout) .build(); HttpGet request = new HttpGet(url); request.setConfig(config); try (CloseableHttpResponse response = client.execute(request)) { return EntityUtils.toString(response.getEntity()); } catch (IOException e) { throw new RuntimeException("请求失败", e); } } } // 使用示例 httpClientHelper.executeWithTimeout("https://api.example.com", 1000, 3000); ``` ### 注意事项 1. **资源释放**:务必使用 `try-with-resources` 或手动关闭响应 2. **连接池管理**:合理配置连接池参数避免资源耗尽 3. **HTTPS支持**:如需发送HTTPS请求,需额外配置SSL上下文[^3] 4. **超时监控**:建议记录请求耗时用于优化超时阈值 5. **异常处理**:捕获 `SocketTimeoutException` 处理超时情况 > 此方案比 `RestTemplate` 的全局超时配置更灵活[^1][^2],实现了请求级别的超时控制。 ### 性能优化建议 1. **连接复用**:使用连接池减少TCP握手开销 2. **超时默认值**:为常用请求设置合理的默认超时 3. **异步请求**:耗时操作使用异步方法避免线程阻塞 4. **熔断机制**:结合Hystrix等实现超时熔断
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值