Writing optional bean dependency injection by bean name - java

I've read some articles on optional bean dependencies, it is usually suggested to use java Optional class, or spring ObjectProvider class.
Those do work, but my case is a little different. What if in my context there are multiple beans of the same type, which annotated with #Qualifier and I don't know if there is a bean I need among them. And I need the one with the specific name.
#ComponentScan(basePackages = "my.package")
public class MyClass {
private final MyOptionalBean myOptionalBean;
MyClass(ObjectProvider<MyOptionalBean> myOptionalBeanObjectProvider) {
this.myOptionalBean = myOptionalBeanObjectProvider.getIfAvailable(() -> null);
}
}
The example above works. But now imagine, that there are multiple MyOptionalBean beans registered in my context, those beans are named. How do I write the similar code as above, but tell spring to look by the name of specific instance?

You can use #Autowired(required = false) and #Qualifier in combination:
MyClass(#Autowired(required = false) #Qualifier("foo") MyOptionalBean myBean)
{
// myBean will be null if no bean with the qualifier exists
}

Related

Bean name resolution in Spring using #Bean and #Qualifier annotations

I just found a behaviour of Spring that I cannot understand. I am using Spring Boot 1.5.x.
In a configuration class, I declared two different beans.
#Configuration
class Config {
#Bean("regularRestTemplate")
public RestTemplate regularRestTemplate(String toLog) {
return new RestTemplateBuilder().setConnectTimeout(100)
.setReadTimeout(100)
.additionalInterceptors((request, body, execution) -> {
log.info("Inside the interceptor {}", toLog);
return execution.execute(request, body);
})
.build();
}
#Bean("exceptionalRestTemplate")
public RestTemplate regularRestTemplate() {
return new RestTemplateBuilder().setConnectTimeout(100)
.setReadTimeout(100)
.build()
}
}
Then, I have a class that should use the bean called exceptionalRestTemplate.
#Component
class Client {
private RestTemplate restTemplate;
#Autowired
public Client(#Qualifier("exceptionalRestTemplate") RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
// Code that uses the injected rest template
}
Since I specified the name of the bean I want to be injected using the #Qualifier annotation, I would expect that Spring injects the bean called exceptionalRestTemplate. However, the bean called regularRestTemplate is actually used during the injection process.
It turns out that the problem was in the name of the methods that declare the beans in the configuration class. Both are colled regularRestTemplate. Changing the second method name, solve the problem.
My question is, why? I know that Spring uses the names of classes and methods annotated with the #Bean or with the #Component, #Service, etc... annotations to give a name to Java object inside the resolution map. But, I supposed that giving a name inside these annotations would override this behaviour.
Does anybody tell me what's going on?
Bean qualifier and bean name are different meanings. You qualified new bean but tried to override it (arguments don't matter). In your application, you cannot override beans so you have the only first one.
You can check this 'theory'. Add a parameter in your configuration
spring.main.allow-bean-definition-overriding=true
and start your application again. After that, you will have only a second bean.
This is an explanation of the collision. But the solution is a separation of beans to different configurations.

How to specify a default bean for autowiring in Spring?

I am coding both a library and service consuming this library. I want to have a UsernameProvider service, which takes care of extracting the username of the logged in user. I consume the service in the library itself:
class AuditService {
#Autowired
UsernameProvider usernameProvider;
void logChange() {
String username = usernameProvider.getUsername();
...
}
}
I want to have a default implementation of the UsernameProvider interface that extracts the username from the subject claim of a JWT. However, in the service that depends on the library I want to use Basic authentication, therefore I'd create a BasicAuthUsernameProvider that overrides getUsername().
I naturally get an error when there are multiple autowire candidates of the same type (DefaultUsernameProvider in the library, and BasicAuthUsernameProvider in the service), so I'd have to mark the bean in the service as #Primary. But I don't want to have the library clients specify a primary bean, but instead mark a default.
Adding #Order(value = Ordered.LOWEST_PRECEDENCE) on the DefaultUsernameProvider didn't work.
Adding #ConditionalOnMissingBean in a Configuration class in the library didn't work either.
EDIT: Turns out, adding #Component on the UsernameProvider implementation classes renders #ConditionalOnMissingBean useless, as Spring Boot tries to autowire every class annotated as a Component, therefore throwing the "Multiple beans of type found" exception.
You can annotate the method that instantiates your bean with #ConditionalOnMissingBean. This would mean that the method will be used to instantiate your bean only if no other UserProvider is declared as a bean.
In the example below you must not annotate the class DefaultUserProvider as Component, Service or any other bean annotation.
#Configuration
public class UserConfiguration {
#Bean
#ConditionalOnMissingBean
public UserProvider provideUser() {
return new DefaultUserProvider();
}
}
You've not posted the code for DefaultUsernameProvider but I guess its annotated as a #Component so it is a candidate for auto wiring, and the same with the BasicAuthUsernameProvider. If you want to control which of these is used, rather than marking them both as components, add a #Configuration class, and create your UsernameProvider bean there:
#Configuration
public class ProviderConfig {
#Bean
public UsernameProvider() {
return new BasicAuthUsernameProvider();
}
}
This bean will then be auto wired wherever its needed

How do I add a bean to Spring context in my library without breaking consumers who have their own instance of that bean?

I have a library which produces beans into a Spring context for use by clients. The beans I produce are configured by Spring. I need to add a new bean to my context in order to satisfy a dependency of a new bean I'm publishing. However, I believe some of my clients already have an instance of this bean and are autowiring it by type. So I have something like this:
// Code in my Library
#Component
public class PublicUtilityClass {
// This is all new code in my library
private NewDependency newDependency;
public void newCapability() {
newDependency.doNewThing();
}
#AutoWired
public void setNewDependency(NewDependency newDependency) {
this.newDependency = newDependency;
}
// Other library code omitted.
}
How can I use Spring to instantiate NewDependency and inject it into PublicUtilityClass without impacting customers who already have a NewDependency bean in their context?
You should look at #Qualifier annotation. Qualifier allows you to have multiple instance of your bean

What are all the #Configuration naming rules for beans?

I have some integration tests that are supposed to mock out one of many beans in my system. To do this, I have a #Configuration that looks like this:
#Configuration
public class MockContext {
#Primary
#Bean
public RealBean realBean() {
return new MockBean();
}
}
I noticed that this method gets used if RealBean is a java class without #Component. But if RealBean is a #Component, I have to change this context method to look like this:
#Configuration
public class MockContext {
#Primary
#Bean
public RealBean getRealBean() {
return new MockBean();
}
}
Can anyone explain why I need to change this method name and where I can find all these rules? It takes a very long time to troubleshoot these "why isn't my MockContext working correctly?" issues.
FWIW, here's how I'm using this context in a test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {RealContext.class, MockContext.class})
#WebAppConfiguration
public abstract class AbstractIntegrationTest {
And my integration test will extend this class. I am using Spring Boot 1.2.4.RELEASE
You can have various beans registered with same type. But they need to have different names.
If you use #Bean annotation without name attribute, name of the bean is extracted from method name (in your case realBean/getRealBean).
When you use #Component annotation without attribute (which specifies bean name), name of the bean is extracted from method name where first letter is lowercased.
So with your first case, you get a name clash. You can't have two beans named realBean.
Your second example is without clash because bean annotated by #Component has name realBean and second bean registered via #Bean has name getRealBean.
#Primary annotation helps Spring choose which bean to pick if there are two of the same type and you inject by type. When you inject by name (usage of #Qualifier annotation), you can inject also secondary instance.

How to declare a Spring bean autowire-candidate="false" when using annotations?

I am using #ComponentScan and #Component to define my spring beans. What I would like is to declare one of these beans to be autowire-candidate=false.
This could be done with this attribute in xml. Isn't there the equivalent in annotations?
The reason I want this is because I have 2 implementations of the same interface and I don't want to use #Qualifier.
EDIT: Using #Primary is a valid work-around, but autowire-candidate seems to me like a useful feature with its own semantics.
Thanks
Looks like Spring refused autowire-candidate=false concept and it no longer supported. There is no analogue with annotations, so #Primary is the best work-around as you noticed.
Another way is to use custom org.springframework.beans.factory.support.AutowireCandidateResolver, which is used in DefaultListableBeanFactory, with logic that exclude undesirable beans from autowire candidates. In such case, the technology will be similar to that used for autowire-candidate=false in SimpleAutowireCandidateResolver.
Since Spring 5.1 , you can configure autowire-candidate in #Bean through autowireCandidate attribute:
#Bean(autowireCandidate = false)
public FooBean foo() {
return newFooBean();
}
You can also use the bean accessor to tune it's visibiltiy.
see Bean visibility
#Configuration
public abstract class VisibilityConfiguration {
#Bean
public Bean publicBean() {
Bean bean = new Bean();
bean.setDependency(hiddenBean());
return bean;
}
#Bean
protected Bean hiddenBean() {
return new Bean("protected bean");
}
}
You can then #Autowire the Bean class and it will autowire the public bean (without complaining about multiple matching beans)
As a class' definition (unless embedded) does not allow private / protected accessor the work around would be to use an #Configuration class that would instantiate all the beans an publish the public beans while hiding the private/protected (instead of directly annotating the classes #Component \ #Service)
Also package-protected accessor may worth a try to hide #Component annotated classes. I don't know if that may work.
We can do this by using the #Qualifier annotations with name mentioned in the #Component annotations
Bean classes -
#Component("fooFormatter")
public class FooFormatter implements Formatter {
public String format() {
return "foo";
}
}
#Component("barFormatter")
public class BarFormatter implements Formatter {
public String format() {
return "bar";
}
}
Injecting bean in service class -
public class FooService {
#Autowired
#Qualifier("fooFormatter")
private Formatter formatter;
}
For more details please refer - https://www.baeldung.com/spring-autowire#disambiguation. Above example taken from this link.

Categories

Resources