java - 当我们在 @Bean 中创建新类并在另一个 @Bean 中使用 @Bean 时会发生什么
问题描述
我有豆子:
@Bean
public Map<String, Integer> currenciesCodesMap(@Value("${readTimeoutMillis}") int readTimeout,
@Value("${connectTimeoutMillis}") int connectTimeout,
UriFactory uriFactory) {
System.out.println("currenciesCodesMap");
RestTemplate restTemplate = getRestTemplate(readTimeout + 1, connectTimeout + 1);
List<Map> maps = Arrays.asList(Objects.requireNonNull(restTemplate.getForObject(uriFactory.getProgressiveCurrencyRates(), Map[].class)));
Map<String, Integer> currenciesCodesMap = maps.stream().collect(Collectors.toMap(
map -> (String) map.get("code"),
map -> (Integer) map.get("id")
));
}
@Bean
public RestTemplate codesConverterRestTemplate(@Value("${readTimeoutMillis}") int readTimeout,
@Value("${connectTimeoutMillis}") int connectTimeout,
UriFactory uriFactory) {
System.out.println("codesConverterRestTemplate");
return getRestTemplate(readTimeout, connectTimeout);
}
@Bean
public RestTemplate getRestTemplate(int readTimeout, int connectTimeout) {
CloseableHttpClient httpClient = HttpClientBuilder.create().disableCookieManagement().build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
factory.setReadTimeout(readTimeout);
factory.setConnectTimeout(connectTimeout);
System.out.println("getRestTemplate");
return new RestTemplate(factory);
}
我不想在每个 bean 中创建新的 RestTemplate,所以我决定在另一个 bean 中创建它并在两个第一个 bean 中注入第三个 bean。在启动时,我看到(使用 System.out.println)我的 bean 只创建了一个(因为它们是单例),但我无法理解它是怎么回事,因为我在前两个 bean 中使用不同的参数来调用第三个 bean。所以我的问题是:这整件事是如何运作的。new RestTemplate(factory) 将调用多少次,如果我在两个具有不同参数的地方使用它,它怎么可能是对第三个 bean 的调用。以这种方式创建 RestTemplate 是否是一种好方法
解决方案
@Configuration
每次一个用 注释的方法@Bean
调用另一个用 注释的方法时,一个类不会实例化一个新对象@Bean
。考虑这个例子:
@Configuration
public class TestConfig {
@Bean
public String bean2(){
String bean = bean1("bean2");
System.out.println("bean2: " + bean);
return bean;
}
@Bean
public String bean3(){
String bean = bean1("bean3");
System.out.println("bean3: " + bean);
return bean;
}
@Bean
public String bean1(@Autowired(required = false) String name){
System.out.println("bean1 " + name);
return name;
}
}
输出如下,因为bean2()
先执行:
bean1 bean2
bean2: bean2
bean3: bean2
这是相关文档:
换句话说,当您定义一个 bean 定义并将其限定为单例时,Spring IoC 容器会创建该 bean 定义所定义的对象的一个实例。此单个实例存储在此类单例 bean 的缓存中,并且该命名 bean 的所有后续请求和引用都返回缓存的对象。下图显示了单例作用域的工作原理:
在这种情况下,带有注释的方法@Bean
是bean 定义。
还:
所有 @Configuration 类在启动时都使用 CGLIB 进行子类化。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器中是否有任何缓存(作用域)bean。
换句话说,调用带有注解的方法@Bean
每次都应该返回相同的 bean,而不管参数如何。在您的情况下,我认为它是未定义的行为,因为除非您使用 or 之类的选项,否则无法保证实例化的depends-on
顺序SmartLifecycle
。
推荐阅读
- machine-learning - 如何减少神经网络中的过拟合?
- java - JobIntentService 及时重复
- asp.net-web-api - .NET Framework WebApi Owin UseOpenIdConnectAuthentication 中的 IdentityServer4 身份验证
- html - 如何使用 CSS 更改注册商标的大小
- javascript - 如何从对象数组中删除某些键具有重复值的项目?
- sql-server - 从本地 AD 组连接到 AZURE DB
- database - 在 Redis 中一段时间后是否可以删除整个集合?
- sas - 使用 SAS 使用 PROC SCORE 更改数据集中的值
- symfony - Encore-dev 服务器 symfony
- excel - 根据 Excel 值从网站地址中提取数据