У меня есть код с именованными бобами
@Bean
@Named("heimdall-uri-supplier")
public URISupplier heimdallEndpointSupplier(CredentialsClientConfig config, EnvInfo envInfo) {
....
}
@Named("vault-uri-supplier")
@Bean
public URISupplier vaultURISupplier(EnvInfo envInfo, CredentialsClientConfig config) {
....
}
Они явно названы, потому что я хочу, чтобы конкретные реализации внедрялись в разные классы-потребители. Эти классы также используют @Named.
В тестах до Spring Boot 2.1
@Bean
@Primary
@Named("heimdall-uri-supplier")
public URISupplier heimdallEndpointSupplier(CredentialsClientConfig config, EnvInfo envInfo) {
return mock of some sort
}
@Named("vault-uri-supplier")
@Bean
@Primary
public URISupplier vaultURISupplier(EnvInfo envInfo, CredentialsClientConfig config) {
return mock of some sort
}
отлично поработал.
Теперь, конечно, Spring Boot 2.1 отключает переопределение. Я знаю, что могу включить его снова, но теоретически я бы предпочел не делать этого.
Но мой "нормальный" обходной путь (do @Bean (name = "testFoo") здесь не сработает, потому что инжектор @Named в потребляющих классах теперь выйдет из строя.
Есть какие-нибудь решения?




Мне это удалось, но это было довольно болезненно. Пришлось использовать BeanDefinitionRegistryPostProcessor. Настроить GenericBeanDefinition уродливо, как грех. Здесь было не так уж и плохо, потому что моки не являются аргументами. Если вам нужно основывать их на введенных или заданных аргументах конструктора, это действительно становится довольно сложным.
/**
* This class basically removes the existing bean definition and substitutes in the mocks.
* Normally we don't need this - we just change the bean name. However because the
* classes use an @Named qualifier, more heroic efforts are needed
*/
public static class OverridePostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(final BeanDefinitionRegistry registry) throws BeansException {
if (registry.isBeanNameInUse("vault-uri-supplier")) {
registry.removeBeanDefinition("vault-uri-supplier");
}
// Note: These are a bear to work with, but in theory you can build
// them from scratch based on injected beans etc. Fortunately these
// two examples just needed a no-args supplier
GenericBeanDefinition g = new GenericBeanDefinition();
g.setBeanClass(URISupplier.class);
g.setInstanceSupplier(this::vaultURISupplier);
registry.registerBeanDefinition("vault-uri-supplier", g);
if (registry.isBeanNameInUse("taskRequestValidator")) {
registry.removeBeanDefinition("taskRequestValidator");
}
g = new GenericBeanDefinition();
g.setBeanClass(TaskRequestValidator.class);
g.setInstanceSupplier(this::taskRequestValidator);
registry.registerBeanDefinition("taskRequestValidator", g);
}
URISupplier vaultURISupplier() {
return new com.opentable.credentials.client.internal.TestVaultConfiguration.MockSupplier();
}
TaskRequestValidator taskRequestValidator() {
return (getTokenRequest, servicePolicy) -> ValidationResult.OK;
}
@Override
public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
/* No op */
}
}
Чтобы было ясно: если бы я не использовал Named, у меня не было бы этой проблемы. Я использую Named, потому что создаю два bean-компонента одного типа, но хочу, чтобы они вводились в разные потребляющие bean-компоненты. По-настоящему УЖАСНЫЙ обходной путь - это ввести Set, а затем взять «правильный», но это хакерский хакерский хакерский прием.