在我的yaml文件中,我的配置值如下:
myapp:
rest-clients:
rest-templates:
- id: myService1
username: chris
password: li
base-url: http://localhost:3000/service1
read-timeout: 2s
connect-timeout: 1s
- id: myService2
username: chris
password: li
base-url: http://localhost:3000/service1
read-timeout: 2s
connect-timeout: 1s
我想让Spring Boot 2应用程序为每个配置项注册一个RestTemplate。
我的配置是bean如下:
@Configuration
@AllArgsConstructor
public class MyAppRestClientsConfiguration {
private MyAppRestClientsProperties properties;
private GenericApplicationContext applicationContext;
private RestTemplateBuilder restTemplateBuilder;
@PostConstruct
public void init() {
properties.getRestTemplates().forEach(this::registerRestTemplate);
}
private void registerRestTemplate(MyAppRestTemplateConfig config) {
// do some work
applicationContext.registerBean(config.getId(), RestTemplate.class, () -> restTemplate)
}
}
问题是,当我通过@Autowire注入注册的RestTemplate时,此配置bean尚未完成初始化。所以没有RestTemplate bean可以被注入。
@Autowired
@Qualifier("myService1")
private RestTemplate client1;
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
- @org.springframework.beans.factory.annotation.Qualifier(value=myService1)
有没有正确的方法来实现此要求?
答案 0 :(得分:0)
在带有注释的方法@PostConstruct
中注册新bean的问题在于,Spring已经超过了Spring生命周期(more info on the Spring life cycle)中的特定时间点。有时,诸如@DependsOn
(已经提到),@Order
或@Lazy
之类的注释可能会有所帮助。但是,正如您提到的那样,您不希望在使用您的库的项目上强加(春季)实现细节,我编写了一个BeanFactoryPostProcessor
,注册了一个RestTemplate
bean:
@Component
public class DemoBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) {
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(RestTemplate.class);
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(Integer.valueOf(configurableListableBeanFactory.resolveEmbeddedValue("${rest-templates[0].read-timeout}")));
factory.setConnectTimeout(Integer.valueOf(configurableListableBeanFactory.resolveEmbeddedValue("${rest-templates[0].connect-timeout}")));
// etc
ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
constructorArgumentValues.addGenericArgumentValue(factory);
genericBeanDefinition.setConstructorArgumentValues(constructorArgumentValues);
String beanId = configurableListableBeanFactory.resolveEmbeddedValue("${rest-templates[0].id}");
((DefaultListableBeanFactory) configurableListableBeanFactory).registerBeanDefinition(beanId, genericBeanDefinition);
}
}
application.yml:
rest-templates:
- id: myService1
username: chris
password: li
base-url: http://localhost:3000/service1
read-timeout: 2000
connect-timeout: 1000
- id: myService2
username: chris
password: li
base-url: http://localhost:3000/service1
read-timeout: 2000
connect-timeout: 1000
伴随测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
@Qualifier("myService1")
private RestTemplate restTemplate;
@Test
public void demoBeanFactoryPostProcessor_shouldRegisterBean() {
String stackOverflow =
restTemplate.getForObject("https://stackoverflow.com/questions/57122343/how-to-create-multiple-beans-same-type-in-one-spring-boot-java-config-class", String.class);
Assertions.assertThat(stackOverflow).contains("How to create multiple beans (same type) in one Spring Boot java config class (@Configuration)?");
}
}
在完全设置应用程序上下文之前调用BeanFactoryPostProcessor
时,我不得不找到另一种方法来检索应用程序属性。我使用方法ConfigurableListableBeanFactory#resolveEmbeddedValue
来检索占位符值,而不是通过@Value
批注或environment#getProperty
注入它们。此外,由于2s
需要一个int值,因此我将属性值2000
重写为HttpComponentsClientHttpRequestFactory
。