首页 > 解决方案 > 当我们在 @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 是否是一种好方法

标签: javaspringrestspring-bootspring-mvc

解决方案


@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


推荐阅读