zzyclark zzyclark - 5 months ago 127
Java Question

Spring autowire multiple service Implementations

I have one base interface and two implementations

public interface AnimalService
{
public void eat();
}

@Service("animalService")
@Transactional
public class AnimalServiceImpl implements AnimalService
{
@Override
public void eat()
{
System.out.println("i'm eating");
}
}

@Service("birdService")
@Transactional
public class BirdServiceImpl extends AnimalServiceImpl
{
public void fly()
{
System.out.println("i'm flying");
}
}


In my main method try to call this two service implementation in this way:

public class test
{
@Autowired
private AnimalService animalService;
@Autowired
@Qualifier("birdService")
private AnimalService birdService;

public static void main(String[] args)
{
animalService.eat();
birdService.eat();
birdService.fly();
}
}


This will give compilation error, since birdService can't find method fly(). Then I thought maybe the reason is i autowire AnimalService instead of BirdServiceImpl, So i change my autowire code from this:

@Autowired
@Qualifier("birdService")
private AnimalService birdService;


change to :

@Autowired
private BirdServiceImpl birdService;


But this will give me a runtime error, which is "can't find bean BirdServiceImpl".
I have google a lot of document, some say use @Resource. But this doesn't work for me. Some say register the bean in Spring Context, while all my bean registration is done by annotation. I don't want to touch Spring Context.

Now My solution is to add a new interface

public interface BirdService extends AnimalService
{
public void fly();
}


And let my BirdServiceImpl to implement this interface

public class BirdServiceImpl extends AnimalServiceImpl extends BirdService
{
public void fly()
{
System.out.println("i'm flying");
}
}


And my main class change to this:

public class test
{
@Autowired
private AnimalService animalService;
@Autowired
private BirdService birdService;

public static void main(String[] args)
{
animalService.eat();
birdService.eat();
birdService.fly();
}
}


Now is ok . But for me, this is not perfect. If I use plain java, i can just write single interface and multiple implementation. In the main method I can choose which implementation to use. Why in spring, i have to build a new interface for each new implementation in order to let my program run.

I want to know is there any better approach for my scenario?

Answer

In your question you are actually exposing two issues:

1. Inheritance issue

This problem doesn't depends on Spring Framework, but is due by your misconception about inheritance. If you declare your service as AnimalService, you obviously can use it only as an AnimalService, regardless its real implementation. If you want to use concrete implementations methods, you need to cast your object.

2. 'Autowiring a class' issue

This should normally work in Spring, so if your code doesn't work depends on your context configuration. Maybe you are also using AOP or transactions in your app, If so, an autoproxy generator is enabled. This could cause your problem.

Take a look at this question: Spring Autowiring class vs. interface?. And note that:

When using autoproxies, you need to program to the interface, not the implementation

3. Just a note

How can you use () at the end of a Java interface/class name?