简体   繁体   中英

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?

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?

As I read in your question, you have already fixed the problem by creating an Interface for the inherited class BirdService . You only complain because you have to create a new Interface...

When I read your question, another question comes to mind: Which AOP are you using? Perhaps you have to add the CGLIB to your classpath (or Maven POM or Gradle).

Reading some of the Spring AOP documentation , I found this:

If the class of a target object that is to be proxied (hereafter simply referred to as the target class) doesn't implement any interfaces, then a CGLIB-based proxy will be created. This is the easiest scenario, because JDK proxies are interface based, and no interfaces means JDK proxying isn't even possible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM