Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend @Service?

Tags:

java

spring

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?

like image 239
Mike Avatar asked Apr 30 '15 04:04

Mike


3 Answers

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;    

    ...

}
like image 89
npe Avatar answered Nov 16 '22 11:11

npe


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.

like image 25
geoand Avatar answered Nov 16 '22 11:11

geoand


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

like image 1
Sunil Dabburi Avatar answered Nov 16 '22 12:11

Sunil Dabburi