hitchhiker
hitchhiker

Reputation: 1319

How to import a bean which comes from a configuration bean conditional on another class?

I'm using a 3rd party dependency in my Spring Boot project (version 2.6.3) which has the following classes:

@ConditionalOnProperty(prefix = "spring.cloud.vault", name = "enabled", matchIfMissing = true)
@AutoConfigureAfter(ApacheHttpClientAutoConfiguration.class)
@Configuration
public class VaultServiceAutoConfiguration {
    // ...
}


@Configuration
@EnableConfigurationProperties({VaultServiceProperties.class})
@ConditionalOnBean(VaultServiceAutoConfiguration.class)
public class VaultServiceFacadeConfiguration {
  private final VaultServiceProperties vaultServiceProperties;

  @Autowired
  public VaultServiceFacadeConfiguration(VaultServiceProperties properties) {
    this.vaultServiceProperties = properties;
  }

  @Bean
  @ConditionalOnMissingBean(VaultServiceFacade.class)
  public VaultServiceFacade vaultServiceFacade(
      @Qualifier("runtime") VaultServiceTemplate vaultServiceTemplate) {
        // ...
  }
}

I set spring.cloud.vault.enabled=true in my bootstrap.yml.

In my project I import the dependency and created a component class:

@Component
public class MyVault {
    private VaultServiceFacade vaultServiceFacade;

    public MyVault(VaultServiceFacade vaultServiceFacade) {
        this.vaultServiceFacade = vaultServiceFacade;
    }
}

in order to use VaultServiceFacade bean which provides methods to interact with Hashicorp Vault instance. The problem is that I get this error: No qualifying bean of type 'other.package.VaultServiceFacade' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

I don't understand why I get his error because my main class is annotated with @SpringBootApplication so that auto-configuration is enabled. In addition if I try to inject VaultServiceAutoConfiguration bean it works:

@Component
public class MyVault {
    private VaultServiceAutoConfiguration v;

    public MyVault(VaultServiceAutoConfiguration v) {
        this.v = v;
    }
}

EDIT: I did notice that the dependency has a file spring.factories with the following code:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  other.package.VaultServiceAutoConfiguration

So maybe only VaultServiceAutoConfiguration is "whitelisted" for auto-configuration, so I need to understand how to allow other beans from other.package to be injected in my project.

EDIT2: I managed to get this working by adding @ComponentScan on the MyVault class:

@ComponentScan("other.package")
@Component
public class MyVault {
    private VaultServiceFacade vaultServiceFacade;

    public MyVault(VaultServiceFacade vaultServiceFacade) {
        this.vaultServiceFacade = vaultServiceFacade;
    }
}

I still don't understand why the ComponentScan works only on when added to MyVault class but is if I scan for this package from main it class it doesn't work:

@SpringBootApplication(scanBasePackages = {"other.package"})
public class Application {

  // ...
}

Upvotes: 3

Views: 2290

Answers (2)

IPP Nerd
IPP Nerd

Reputation: 1124

As the default value for the property is already true, I would not set it at all. In order to explicitly configure the Facade try to @Import(VaultServiceFacadeConfiguration.class) in your SpringBootApplication.

Please check your dependencies. "other.package" looks very strange and does not look like a package name used by HashiCorp.

Upvotes: 0

edu
edu

Reputation: 468

@ComponentScan just works because you're very directly asking Spring to scan said package for any beans which it straight up does.

I can't tell why @SpringBootApplication(scanBasePackages = {"other.package"}) doesn't work without trying it for myself but I guess the error you get is that the other.package beans get created and instead all other beans fail to exist (after all other.package is not the base package).

Finally, why doesn't you preferred conditional approach work?

Hard to tell without a simplified sample app to work with. I doubt it's because of VaultServiceAutoConfiguration, I've created a simplified project where the offending bean lies directly in said class and it loads just fine. Try moving your VaultServiceFacade definition to VaultServiceAutoConfiguration and remove @AutoConfigureAfter(ApacheHttpClientAutoConfiguration.class), if that works you know it's something in VaultServiceFacadeConfiguration (or @AutoConfigureAfter(ApacheHttpClientAutoConfiguration.class) itself, maybe @EnableConfigurationProperties({VaultServiceProperties.class})?

Also, I used application.properties instead of bootstrap.yml but that's unlikely to be it.

Feel free to send a sample simplified project to work on and test your assumptions

Upvotes: 2

Related Questions