Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

211
Views
Spring autowiring based on service availability

I have a need of conditionally creating one of three possible implementations of a service depending upon the environment detected by a Spring application at runtime. If Service A is available, then I want to create a concrete implementation class that uses Service A as a dependency. If Service A is not available, then I want to create an implementation using Service B as a dependency. And so-on.

Classes which depend on the implementation will Autowire the Interface and not care what the underlying Service was that got selected for the particular environment.

My first stab at this was to implement multiple @Bean methods which either return a bean or null, depending on whether the Service is available, and to then have a separate @Configuration class which @Autowire(required=false) the two possible services, conditionally creating the implementation depending on which of the @Autowired fields was not-null.

The problem here is that when required=false, Spring doesn't appear to care whether it waits around for candidates to be constructed; that is to say, the class which tries to pick the implementation might be constructed before one or both of the required=false Beans gets constructed, thus ensuring that one or both might always be null, regardless of whether it may manage to initialize correctly.

It kind of feels like I'm going against the grain at this point, so I'm looking for advice on the "right" way to do this sort of thing, where a whole set of beans might get switched out based on the availability of some outside service or environment.

Profiles don't look like the right answer, because I won't know until after my Service beans try to initialize which implementation I want to choose; I certainly won't know it at the time I create the context.

@Order doesn't achieve the goal either. Nor does @Conditional and testing on the existence of the bean (because it still might not be constructed yet). Same problem with FactoryBean- it does no good to check for the existence of beans that might not have been constructed at the time the FactoryBean is asked to create an instance.

What I really need to do is create a Bean based on the availability of other beans, but only AFTER those beans have at least had a chance to try to initialize.

about 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Spring Profiles is your friend. You can set the current profile by way of environmental variable, command-line argument, and other methods. You can annotate a Spring-managed component so that it's created for a certain profile.

Spring Profiles from the Spring Documentation

about 4 years ago · Santiago Trujillo Report

0

Well in this case it turned out to be a tangential mistake that influenced the whole wrong behavior.

To give some background, my first, naive (but workable) approach looked like this:

@Autowired(required=false)
@Qualifier(RedisConfig.HISTORY)
private RLocalCachedMap<String, History> redisHistoryMap;

@Autowired(required=false)
@Qualifier(HazelcastConfig.HISTORY)
private IMap<String, History> hazelcastHistoryMap;

// RequestHistory is an interface
@Bean
public RequestHistory requestHistory() {
    if (redisHistoryMap != null) {
        return new RedisClusteredHistory(redisHistoryMap);
    } else if (hazelcastHistoryMap != null) {
        return new HazelcastClusteredHistory(hazelcastHistoryMap);
    } else {
        return new LocalRequestHistory();  // dumb hashmap
    }
}

In other @Configuration classes, if the beans that get @Autowired here aren't available (due to missing configuration, exceptions during initialization, etc), the @Bean methods that create them return null.

The observed behavior was that this @Bean method was getting called after the RLocalCachedMap<> @Bean method got called, but before Spring attempted to create the IMap<> by calling its @Bean method. I had incorrectly thought that this had something to do with required=false but in fact that had nothing to do with it.

What actually happened was I accidentally used the same constant for both @Bean names (and consequently @Qualifiers), so presumably Spring couldn't tell the difference when it was calculating its dependency graph for this @Configuration class... because the two @Autowired beans appeared to be the same thing (because they had the same name).

(There's a secondary reason for using @Qualifier in this case, which I won't go into here, but suffice it to say it's possible to have many maps of the same type.)

Once I qualified the names better, the code did exactly what I wanted it to, albeit in a way that's somewhat inelegant/ugly.

At some point I'll go back and see if it looks more elegant / less ugly and works just as well to use @Conditional and @Primary instead of the if/else foulness.

The lesson here is that if you explicitly name beans, make absolutely sure your names are unique across your application, even if you plan to swap things around like this.

about 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!