Here is a sample class below:
@Service("testService")
public class TestService {
public String something() {
return "abc";
}
}
I want to extend the class and let the container know that it needs to pick up my extended class from now.
@Service("extendedTestService")
public class ExtendedTestServiceMock extends TestService {
@Override
public String something() {
return "xyz";
}
}
Test class:
public class TestClass extends SpringTest {
@Autowired
@Qualifier("extendedTestService")
private ExtendedTestService testService;
public void testMethod() {
......
}
}
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [TestService] is defined: expected single matching bean but found 2: ExtendedTestServiceMock,testService
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:865) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:770) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:489) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
... 91 common frames omitted
How to resolve it?
Try using interfaces.
public interface TestService {
String something();
}
Implementations:
@Service
@Qualifier("testService")
public class TestServiceImpl implements TestService { ... }
@Service
@Qualifier("testServiceMock")
public class TestServiceMockImpl implements TestService { ... }
And the test class:
public class TestClass extends SpringTest {
@Autowired
@Qualifier("extendedTestService")
private TestService testService;
...
}
One solution that would work in your case is the @Primary
annotation.
Your TestServiceMockImpl
would look like:
@Service("extendedTestService ")
@Primary
public class ExtendedTestServiceMock extends TestService {
@override
public String something() {
return "xyz";
}
}
Check out this for more details on @Primary
I however suggest that you don't follow the above solution (since this will get out of hand very quick if you start using @Primary everywhere), that you instead take a look at Spring Profiles
There are a lot of way you could create your Spring configuration using profiles, but regardless of how you end up configuring the beans, the end result would be a more clean design.
If you have an identifier to help you decide which service to initialize, then you can use ConditionlOnProperty
annotation
Ex:
@Service
@ConditionlOnProperty(value = "test.service.extension.enabled")
public class TestService {
}
@Service
@ConditionlOnProperty(value = "test.service.extension.enabled", havingValue = "false")
public class ExtendedTestServiceMock extends TestService {
}
If you want to use the extended test service, you can set the property test.service.extension.enabled=true
in your application.properties
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With