【问题标题】:Best way to dynamically resolve dependencies in Java Spring?在 Java Spring 中动态解决依赖关系的最佳方法?
【发布时间】:2018-12-14 20:49:12
【问题描述】:

假设我有这样的代码结构:

public class NotificationService {
     public void send(Notification notification) {
         // call other services and send the notification
     }
}

public class OrderNotification implements Notification {

    @Autowired
    public TranslationService translationService;

    private String orderNumber;

    public OrderNotification(String orderNumber) {
        this.orderNumber = orderNumber;
    }

    public String getMessage() {
        return translationService.trans('notification.order', new Object[]{orderNumber});
    }
} 

所以,我的目标是以这种方式使用NotificationService

notificationService.send(new OrderNotification(orderNumber));

但我知道上面的代码不起作用,因为 translationService 将无法解析。

我的目标是将自定义参数传递给我的Notification 类,并能够在该类中使用服务。春天最好的方法是什么?

【问题讨论】:

  • 看看这个答案,看看如何在手动创建的对象上初始化 Autowired 字段:*.com/a/37463747/3876196
  • @AntoineB 我见过。它在春季被认为是一种好的做法吗?
  • 既然这是唯一的方法,我相信是的。
  • 还要检查那个*.com/a/6739606/8049667

标签: java spring


【解决方案1】:

我知道下面不是您问题的正确答案。然而,结合EntitiesServices 是一种糟糕的设计模式。 Entity 应该只包含有关对象的信息而不是业务逻辑。 Service 包含所有业务逻辑。

您需要将您的Service 与您的Entity 分开。

OrderNotification 看起来像一个常规实体。实体不应包含业务逻辑。您需要针对业务逻辑的特定服务。

public class OrderNotification implements Notification {

    private String orderNumber;

    public OrderNotification(String orderNumber) {
        this.orderNumber = orderNumber;
    }

    public String getMessage() {
        return "Order number: " + orderNumber;
    }

    //Getter & Setters
    ...
} 

@Service
public class NotificationService {

    @Autowired
    public TranslationService translationService;

    public void send(Notification notification) {
        //I do not know what trans accepts, so I assume it can accept Notification
        translationService.trans(notification.getMessage());
    }
}

如果你真的需要将实体和服务结合起来——那么我推荐这种方法:

@Service
public class Master{

    @Autowired
    NotificationService notificationService

    public void testMethod(){
        Notification notification = notificationService.createOrder("order1");
        notificationService.send(notification);
    }

}

@Service
public class NotificationService {

    @Autowired
    public TranslationService translationService;

    public Notification createOrder(String orderNumber){
        return new OrderNotification(orderNumber, translationService);
    }

    public void send(Notification notification) {
        // call other services and send the notification
        notification.getMessage();
    }
}


public class OrderNotification implements Notification {

    private TranslationService translationService;

    private String orderNumber;

    //I have changed this constructor to accept TranslationService.
    public OrderNotification(String orderNumber, TranslationService translationService) {
        this.orderNumber = orderNumber;
        this.translationService = translationService;
    }

    public String getMessage() {
        return translationService.trans('notification.order', new Object[]{orderNumber});
    }
} 

【讨论】:

  • 完全同意。它不应该以这种方式使用。在这种情况下,OrderNotification 类应该是 POJO。
【解决方案2】:

你有几个选择:

  1. 配置 AOP 和加载时间编织以处理使用 new 关键字创建的对象上的 Spring 注释。这在文档5.8.1. Using AspectJ to dependency inject domain objects with Spring 中有解释。

  2. OrderNotification 声明为原型作用域bean,并使用BeanFactory.getBean(Class<T> requiredType, Object... args) 方法从上下文中获取每个实例。

    String orderNumber = "123";
    OrderNotificaton = factory.getBean(OrderNotificaton.class, orderNumber);
    
  3. 删除@Autowired 并使用普通的构造函数注入。

    public OrderNotification(TranslationService translationService, String orderNumber) {
        this.translationService = Objects.requireNonNull(translationService);
        this.orderNumber = Objects.requireNonNull(orderNumber);
    }
    

如果您只需要简单的@Autowired,我会选择选项 3。这是最简单的方法,并且可以更轻松地编写单元测试,因为您不必依赖 Spring。

【讨论】:

  • 3 选项看起来不错,但是将TranlationService 注入到我要使用通知的每个类中非常烦人。