【问题标题】:Spring autowire multiple service ImplementationsSpring自动装配多个服务实现
【发布时间】:2015-03-29 14:45:15
【问题描述】:

我有一个基本接口和两个实现

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");
    }
}

在我的 main 方法中尝试以这种方式调用这两个服务实现:

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();
    }
}

这会产生编译错误,因为birdService 找不到方法fly()。然后我想可能是因为我自动装配 AnimalService 而不是 BirdServiceImpl,所以我改变了我的自动装配代码:

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

改为:

   @Autowired
   private BirdServiceImpl birdService;

但这会给我一个运行时错误,即“找不到 bean BirdServiceImpl”。 我有很多文档,有人说使用@Resource。但这对我不起作用。有人说在 Spring Context 中注册 bean,而我所有的 bean 注册都是通过注解完成的。我不想碰 Spring Context。

现在我的解决方案是添加一个新界面

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

并让我的 BirdServiceImpl 来实现这个接口

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

我的主班变成了这样:

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

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

现在好了。但对我来说,这并不完美。如果我使用纯 java,我可以只编写单个接口和多个实现。在主要方法中,我可以选择要使用的实现。为什么在春天,我必须为每个新实现构建一个新接口才能让我的程序运行。

我想知道我的场景有没有更好的方法?

【问题讨论】:

  • 您的代码原样也不适用于纯 java。 AnimalService 没有 fly 方法,无论您使用什么 Spring 或普通 java(哎呀 spring 是 java),它都找不到该方法。 BirdServiceImpl 的注入将失败,因为根据您的配置,将创建一个代理来应用 AOP。默认情况下使用接口,因为您正在实现一个接口,所以您的 bean 将是一个 AnimalService 但不是其中一个实现,这将是一个实现所有接口的动态代理。哪个,恕我直言,我应该像你应该对接口编程一样。
  • 你是对的,如果我使用普通的java,仍然不能调用fly(),除非我像这样初始化bean:BirdServiceImpl birdService = new BirdServiceImpl();

标签: java spring spring-mvc


【解决方案1】:

正如我在您的问题中所读到的,您已经通过为继承类 BirdService 创建一个 Interface 来解决问题。你只是抱怨,因为你必须创建一个新的接口......

当我阅读您的问题时,我想到了另一个问题:您使用的是哪种 AOP?也许您必须将 CGLIB 添加到您的类路径(或 Maven POM 或 Gradle)中。

看了一些Spring AOPdocumentation,发现了这个:

如果要代理的目标对象的类(以下简称 简称为目标类)没有实现任何 接口,然后将创建一个基于 CGLIB 的代理。 这是 最简单的方案,因为 JDK 代理是基于接口的,并且没有 接口意味着 JDK 代理甚至是不可能的。

【讨论】:

    【解决方案2】:

    在您的问题中,您实际上暴露了两个问题:

    1.继承问题

    这个问题不依赖于 Spring Framework,而是由于您对继承的误解。 如果您将您的服务声明为AnimalService,那么您显然只能将其用作AnimalService,而不管它的实际实现。 如果你想使用具体的实现方法,你需要转换你的对象。

    2。 “自动装配类”问题

    这通常应该在 Spring 中工作,所以如果您的代码不起作用取决于您的上下文配置。也许你也在你的应用程序中使用 AOP 或事务, 如果是这样,则启用自动代理生成器。这可能会导致您的问题。

    看看这个问题:Spring Autowiring class vs. interface?。请注意:

    使用自动代理时,您需要对接口进行编程,而不是对实现进行编程

    3.只是一个注释

    如何在 Java 接口/类名的末尾使用 ()

    【讨论】:

    • 是的,我也为我的每个服务使用事务注释,我想我需要检查我的 AOP。我已经更新了我的代码
    • 我认为你们两个的答案都是正确的,这里我还有一个问题,实际上,我所有的事务注释都设置为只读,这意味着我不能写入数据我的数据库通过我的服务层?
    • 我觉得如果真的要暂停交易,需要在注解中加上propagation=Propagation.REQUIRED。
    猜你喜欢
    • 1970-01-01
    • 2019-01-16
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-11
    • 2013-08-22
    • 1970-01-01
    相关资源
    最近更新 更多