Overriding and extending bean if application run on given profile - spring

So let's say I've main bean annotated with #Service which is injected in another services with #Autowired.
#Service
#Order(100)
class MainService() {
fun helloWorld() = "Hello"
}
And I would like to extend this service when running with another profile fg. ("custom"). So I have service as below:
#Service("mainService")
#Order(1)
class CustomService: MainService() {
override fun helloWorld() = "Hello custom"
}
But I'm getting this exception:
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'mainService' for bean class [MainService] conflicts with existing, non-compatible bean definition of same name and class [CustomService]
Have you any idea how could I extend and override bean under the same name? Its because I need to autowire it somewhere else

You can use configuration beans to do this:
#Configuration
#Profile("dev")
public class MainServiceDev {
#Bean
public MainService mainService() {
return new MainService();
}
}
#Configuration
#Profile("custom")
public class MainServiceCustom {
#Bean
public CustomService customService() {
return new CustomService();
}
}

Related

Circular dependency error for beans in Spring org.springframework.beans.factory.BeanCurrentlyInCreationException:

I have a classes defined as follows :
#Configuration
public class TestConfig extends IntegrationTestConfig {
#Bean
public TestUserManager testUserManager() {
...
}
#Configuration
#Import({BaseTestConfig.class})
public class IntegrationTestConfig {
#Autowired
private TestUserManager ssbTestUserManager;
#Bean
#DependsOn({"testUserManager"})
public TestDeviceFactory testDeviceFactory() {
return new TestDeviceManager();
}
#Configuration
public class BaseTestConfig {
#Bean
public TestUserManager testUserManager() {
...
}
}
When I try to execute it, it gives the error :
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testConfig': Unsatisfied dependency expressed through field 'ssbTestUserManager'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'testUserManager': Requested bean is currently in creation: Is there an unresolvable circular reference
How can I fix this issue? I am not able to override the testUserManager() method in TestConfig since it does not directly inherit from BaseTestConfig. IntegrationTestConfig and BaseTestConfig are imported as libraries so I dont have much control there.
I think #DependsOn({"testUserManager"}) is causing issue but not sure how I can resolve it
A circular reference occurs when you have a chain of beans that depends together and a cycle created in the dependency graph.
So, in your case, you've defined an instance of TestUserManager in TestConfig class and uses it in the parent class and it creates a cycle!
The solution is defininig abstract getTestUserManager method in the parent class and override it in the child class with #Bean annotation (If you are using it in other parts of code). You can use that method in your parent class instead of using the variable you've defined.
IntegrationTestConfig:
#Configuration
public abstract class IntegrationTestConfig {
public abstract TestUserManager getTestUserManager();
}
TestConfig:
#Configuration
public class TestConfig extends IntegrationTestConfig {
#Bean
#Override
public TestUserManager getTestUserManager() {
return new TestUserManager();
}
}

Getting a bean with all its dependencies using Spring ServiceFactoryBean

I have custom factory class to get a bean using Spring's service factory bean. However, the bean that I want to get from this factory bean, has nested bean dependencies. My question is how do I get a bean from this factory with all its nested dependencies met?
Spring Config :
<bean id="beanFactory"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface" value="com.example.MyBeanFactory">
</bean>
Public class MyBean
{
JobBuilder builder;
}
Public interface MyBeanFactory
{
MyBean getMyBean();
}
Now when I do MyBeanFactory.getMyBean()... I want have to have MyBean instance that includes JobBuilde instance, inside it.
You can treat the implementation class as any other class in the Spring application - just autowire any dependencies that you need. One suggestion I have is to define an interface for the bean class. Example:
public interface MyBean {
void doStuff();
}
Provide an implementation:
#Component("defaultMyBean")
public class DefaultMyBean {
#Autowire
JobBuilder jobBuilder;
public void doStuff() {
// do stuff with jobBuilder
}
}
And then the factory interface:
public interface MyBeanFactory {
MyBean getMyBean();
}
Configuration remains unchanged.

Spring bean with same method name but different qualifier fail to load

I have two Spring Configuration classes defined as follows
#Configuration
public class ClsA {
#Bean
#Qualifier("ClasA")
public String getSomething(){
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean
#Qualifier("ClsB")
public String getSomething(){
return "somethingB";
}
}
Both have the same method name. Even though qualifiers are different, the application doesn't load as it only injects one and wherever the other one is injected, if fails with noBeanDefinition exception let's say for ClsB bean qualifier.
When I keep the method name different and everything loads hunky dory.
Is this behavior normal ? Why doesn't spring load these beans just fine as they have different qualifiers ?
The #Qualifier annotation is supposed to be used at injection points to resolve ambiguity as to which bean to inject. But in the example, you use it at bean declaration site. At declaration site, you can give a name to each bean by specifying it in #Bean annotation. So if you leave your methods with the same name, then a valid example can be as follows:
#Configuration
public class ClsA {
#Bean("ClasA")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("ClasB")
public String getSomething() {
return "somethingB";
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ClsB.class, ClsA.class})
public class ClsTest {
#Autowired
#Qualifier("ClasA") //this is the place where #Qualifier can be used
String smthA;
#Autowired
#Qualifier("ClasB")
String smthB;
#Test
public void test() {
System.out.println(smthA);
System.out.println(smthB);
}
}
Output:
somethingA
somethingB
1. Customize Bean Naming
Each bean name must be unique.
1.3.1. Naming Beans
1.12.3. Using the #Bean Annotation > Customizing Bean Naming
#Configuration
public class ClsA {
#Bean("clasAText")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("clsBText")
public String getSomething() {
return "somethingB";
}
}
2. (Optional) Add qualifier metadata
1.10.5. Defining Bean Metadata within Components
#Configuration
public class ClsA {
#Bean("clasAText")
#Qualifier("clsA")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("clsBText")
#Qualifier("clsB")
public String getSomething() {
return "somethingB";
}
}
3-1. Inject by name
1.9.7. Injection with #Resource
#Component
public class MyComponent {
#Resource(name = "clasAText")
private String text;
// ...
}
Note:
if you intend to express annotation-driven injection by name, do not primarily use #Autowired (snip). Instead, use the JSR-250 #Resource annotation
3-2. Inject by qualifier
1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers
If qualifiers have been added, these are available.
#Component
public class MyComponent {
#Autowired
#Qualifier("clsA")
private String text;
// ...
}
This answer is a fallback behavior. Name and qualifier are different one.
1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers
For a fallback match, the bean name is considered a default qualifier value.

Does registering bean inside #Component class respect #Scope?

This website says beans registered inside component classes are not cglib proxied and do not go through the spring container. So does this mean if I register a bean inside a component class (snippet below), adding #Scope("request") wont make any difference, and a new instance of AnotherBean will always be created whenever testBean.anotherBean() is called from some external class?
#Component
public class TestBean {
#Bean
#Scope("request")
public AnotherBean anotherBean() {
return new AnotherBean();
}
}
The bean that is not cglib proxied is the #Component itself, not the bean registered using the #Bean annotation. If you are not calling the anotherBean method explicitly, it won't make a difference because the proxy is used to return the bean when the method annotated with #Bean is called. See the example
The bean testBeanComponent is not cglib proxied :
#Component
public class TestBeanComponent {
#Bean
#Scope("request")
public AnotherBeanComponent anotherBeanComponent() {
return new AnotherBeanComponent();
}
}
The bean testBeanConfiguration is cglib proxied :
#Configuration
public class TestBeanConfiguration {
#Bean
#Scope("request")
public AnotherBeanConfiguration anotherBeanConfiguration() {
return new AnotherBeanConfiguration();
}
}
What it mean :
#Service
public class TestService {
#Autowired //Inject a normal bean
private TestBeanComponent testBeanComponent;
#Autowired //Inject a proxy
private TestBeanConfiguration testBeanConfiguration;
public void test() {
//Calling anotherBeanComponent always return a new instance of AnotherBeanComponent
testBeanComponent.anotherBeanComponent()
.equals(testBeanComponent.anotherBeanComponent()); // is false
//Calling anotherBeanConfiguration return the bean managed by the container
testBeanConfiguration.anotherBeanConfiguration()
.equals(testBeanConfiguration.anotherBeanConfiguration()); // is true
}
}
But if you are injecting the bean instead of using the method, everything will work as you expected :
#Service
public class TestService2 {
#Autowired //Inject a proxy with scope request
private AnotherBeanComponent anotherBeanComponent;
#Autowired //Inject a proxy with scope request
private AnotherBeanConfiguration anotherBeanConfiguration;
}

Using #Qualifier and #Bean together in Java Config Spring

I have follow code
interface Drivable {
}
#Component
class Bmw implements Drivable {
}
#Component
class Mercedes implements Drivable {
}
class Driver {
private Drivable drivable;
public Driver(Drivable drivable) {
this.drivable = drivable;
}
}
And Spring Java Config
#Configuration
#ComponentScan
class CarConfig {
#Bean
#Qualifier("mercedes")//the code won't work
public Driver getDriver(Drivable drivable) {
return new Driver(drivable);
}
#Bean//I've added the bean
public Drivable getMercedes() {
return new Mercedes();
}
}
Can I use #Qualifier annotation with #Bean annotation if I want to specify type of object that should pass to method? I can't find in Spring doc how I can solve the problem. Thx.
I think you got the usage of #Qualifier bit wrong.
If we have more than one bean that qualifies for spring injection, then we use #Qualifer to specify which needs to be used for injection.
In this case you have two beans Bmw and Mercedes both implementing Drivable interface.
Presuming I got your intent correct, you want spring to inject Mercedes bean into the Driver object.
So for that, you need to specify public Driver getDriver(#Qualifier("mercedes") Drivable drivable) in the CarConfig class.
#Configuration
#ComponentScan
class CarConfig {
#Bean
public Driver getDriver(#Qualifier("mercedes") Drivable drivable) {
return new Driver(drivable);
}
And then you can use AnnotationConfigApplicationContext to load the spring context and subsequently get the Driver bean as below:
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(CarConfig.class);
Driver bean = ctx.getBean(Driver.class);
}
}
Just to extend the example, let us say if you want to create a Driver bean for each of Bmw and Mercedes then the sample code would be:
#Configuration
#ComponentScan
class CarConfig {
#Bean(name="mercedesDriver")
public Driver getMercedesDriver(#Qualifier("mercedes") Drivable drivable) {
return new Driver(drivable);
}
#Bean(name="bmwDriver")
public Driver getBmwDriver(#Qualifier("bmw") Drivable drivable) {
return new Driver(drivable);
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(CarConfig.class);
System.out.println(Arrays.asList(ctx.getBeanNamesForType(Driver.class)));
Driver mercedesBean = ctx.getBean("mercedesDriver", Driver.class);
Driver bmwBean = ctx.getBean("bmwDriver", Driver.class);
}
}
#Configuration
#ComponentScan
class CarConfig {
#Bean
public Driver mercedesDriver(Drivable bmw) {
return new Driver(bmw);
}
// same as
#Bean("mercedesDriver")
public Driver getDriver(Drivable bmw) {
return new Driver(bmw);
}
.....
#Autowired private Driver mercedesDriver;
In Spring you an relate on dependency injection by name. In the first example the name is defined by the method name. In the second example the method name is not important, because wie name the bean through the Annotation. Its very easy and simple. Not really need for #Qualifier. Only if the name should be different, but why?
You could define qualifiers for each specific implementations of interface Drivable. Once you did, now you could autowire them into CarConfig class and you have to be create Beans for each Drivers(Mercedz & Benz) along with qualifier names.
Find below Implementation:
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Bmw {
}
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Mercedes {
}
Now your Interface Implementations of Driver should be annotated with Qualifiers as below
#Compoenent
#Bmw
public interface Bmw implements Drivable{
}
#Component
#Mercedes
public interface Mercedes implements Drivable{
}
Your CarConfig class should be as below:
#Configuration
public class CarConfig{
#autowire
#Bmw
private Drivable bmwDriver;
#autowire
#Mercedes
private Drivable mercedesDriver;
#Bean
public Bean getBmwDriver(){
return new Bmw(bmwDriver);
}
#Bean
public Bean getMercedesDriver(){
return new Mercedes(mercedesDriver);
}
}
NOTE: if you are creating bean with #Bean, it will be injected byType if there is duplicates then it will injected byName. we no need to mention #Bean(name="bmwDriver") .
so you can directly use qualifier("bmwDriver") wherever you need in classes.

Resources