您如何处理在运行时与依赖注入框架一起创建的对象?

How do you handle objects created at runtime in conjunction with Dependency Injection frameworks?

我喜欢依赖注入框架,以及它们如何让我请求一切都以一个对象开始。所有连接都是在第一次请求 "master" 对象时进行的。 但是,有些对象应该在运行时创建,例如基于用户输入。有时那些新创建的对象应该在框架创建的其他对象之间共享。

我目前的方法是让 "uninitialized" 对象由框架注入。在运行时,我尽快使用 setter 方法设置对象。

我不喜欢这种方法的地方是,setter 方法实际上只被调用一次,以后再也不会被调用。这使我无法将字段声明为最终字段。我现在不知道如何在所有必要信息可用之前创建对象,同时又不会失去 DI 框架的所有好处。

我是 DI 新手。有什么好的模式吗?


示例:

// The service is used through out the application
interface Service {
  makeRequest()
}

用户输入凭据后我想做什么:

new ConcreteService(username, password)
// but now I need to inject the concrete servive manually everywhere I need it!

我目前在做什么:

interface Service {
   makeRequest()
   setCredentials(username, password)
}
// service can be injected by framework, but I don't like setter methods
// that much (and they can pollute the interface)

一种方法是使用工厂。

例如,假设您有这个 class...

public class UserBean {

    private int userId;
    private UserService userService;
    // other resources / dependency fields etc

    public UserBean(int userId, UserService userService, ...other dependencies...) {
        this.userService = userService;
        this.userId = userId;
        this.... = ...
    }

    // ...getter for userId maybe etc...

    // Some method that uses the user's data AND the component/service you want to inject from Spring...
    public void incrementHitCount() {
        userService.incrementHitCount(userId);
    }
}

...其中 "userService" 是您想要的由 IoC 容器管理的内容。如果您有一个组件需要创建其中之一,例如...

@Component
public class UserHitCountIncrementerThing {
    public ResponseBean doThatThing(HttpServletRequest request) {
         int userId = request.<get the user Id from it...>
         UserBean userbean = new UserBean(userId, /* need dependencies here! */);
         ...do stuff...
    }
}

您可以在该 bean 的所有服务中只使用 @Autowire,或者您可以创建一个工厂,并且只使用 @Autowire 一个,例如...

@Component
public class UserBeanFactory {
    @Autowired
    private UserService userService
    //...other @Autowired dependencies...

    public UserBean createUser(int userId) {
       return new UserBean(userService, ...etc..., userId);
    }
}

现在只需在需要的地方使用它,例如...

@Component
public class UserHitCountIncrementerThing {
    @Autowired
    private UserBeanFactory userFactory;

    public ResponseBean doThatThing(HttpServletRequest request) {
         int userId = request.<get the user Id from it...>
         UserBean userbean = userFactory.createUser(userId);
         ...do stuff...
    }
}

这就是你想要的吗?

希望对您有所帮助。

我在依赖注入方面的大部分经验都来自于 C#,但我相信无论使用何种语言,这个概念都是一样的。

我从发帖人那里了解到,他正试图 "persist" 依赖注入容器中的信息,以便稍后检索信息。

这种方法的问题在于,在多线程场景中,您用来保存信息的依赖项的值可能会被另一个线程覆盖。之所以会发生这种情况,是因为依赖项注入容器通常包含对象的单个实例,只要您需要,它就会 returned 给您。因此,您需要确保您的设计是线程安全的。

根据我的经验,使用依赖注入容器来维护状态是不好的。

您在依赖项注入容器中注册的是提供 "service" 且不保持任何状态的对象。

您用来保存信息的对象通常是业务对象。这些业务对象应该只用 "new" 实例化(没有依赖注入容器),以通常的方式填充它们(使用 setter 或初始化方法或构造函数)并作为操作签名的一部分传递您的服务公开。

注意:您可以将依赖项注册为 "transient",这会在您每次请求依赖项时告诉依赖项注入容器 return 一个新实例。这将避免显式使用 "new" 关键字的需要,并在使用模拟框架编写单元测试时为您提供更多控制。

希望对您有所帮助!