在没有 DI 框架的情况下在服务中注入存储库

Inject repository in service without DI framework

我需要创建一个简单的 REST API 对资源进行基本的 CRUD 操作,不使用 Spring 但 Java.I 使用 JAX-RS(Jersey 实现)和Jetty 作为嵌入式 servlet container.I 使用 JPA(Hibernate 实现)和 H2 内存数据库。我不使用任何 DI 框架,所以我使用 new().

完成所有 DI "manually"

下面是一个具有 POST 端点的 JAX-RS 服务。我已将存储库创建为服务内的静态最终变量。 BookRepository 是一个接口,BookRepositoryImpl 是这个存储库的实现。我想知道这是否是最好的方法。如果我使用 Spring Autowired 注释执行此操作,我将拥有一个单例存储库,因此我认为模拟它的唯一方法是使用静态最终变量。 当容器运行时,是否为每个请求(线程)创建一个单独的 BookService 实例? 那么多个线程可以访问 bookRepository 的单个副本吗? Autowired 和单例作用域不就是这样吗?

@Path("/books")
public class BookService {

private static final BookRepository bookRepository = new BookRepositoryImpl();

@POST
@Path("")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Book registerBook(Book b) {
    return bookRepository.saveBook(b);
}
}

在没有 DI 容器的情况下应用依赖注入是一种通常称为 Pure DI 的做法。通过这种方法,您可以应用面向对象设计和 DI 的相同原则、实践和模式。但是,不是使用 DI 容器连接所有内容,而是在应用程序的启动路径中,使用 new 关键字手动构建对象图。

纯 DI 是练习 DI 的一种常见且有效的方法 - DI 容器很有用,但 可选 工具。

然而,这不是您目前正在练习的方法。您 没有 将您的依赖项注入他们的消费者。通过在 BookService class 中创建 BookRepositoryImpl,您正在应用 Control Freak anti-pattern, with is a special form of a Dependency Inversion Principle violation. This tightly couples the BookRepositoryImpl class to the BookService class, which will likely cause maintainability issues, because BookRepositoryImpl is a Volatile Dependency。易失性依赖是我们引入抽象和使用依赖注入的原因。

此外,静态字段的使用只会放大痛苦,因为如果 BookRepositoryImpl(或其依赖项之一)不是线程安全的,这可能会导致线程安全问题。

因此,与其将 BookRepositoryImpl 紧密耦合到 BookService,不如将 BookRepository 抽象注入到 BookService 的构造函数中。这使两个组件保持松耦合,并为您提供松耦合带来的所有好处:

@Path("/books")
public class BookService {

    private final BookRepository bookRepository;

    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    @POST
    @Path("")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Book registerBook(Book b) {
        return bookRepository.saveBook(b);
    }
}

但是,这确实意味着您应该覆盖 REST API Web 框架创建该服务的方式。如果此类框架具有默认构造函数,则它们通常只能代表您创建实例。我必须承认我没有使用 JAX-RS 的经验,但大多数框架都允许覆盖其根 classes 的创建。例如,使用 Microsoft ASP.NET MVC 框架,您可以实现自定义 IControllerFactory,并替换框架的默认实现。在您的自定义工厂中,您将手动创建完整的树,其中包含普通的 Java.

public object create(Type controllerType)
{
    if (controllerType == typeof(HomeService))
        return
            new HomeService(
                new PersonsRepositoryImpl(this.connectionString));

    if (controllerType == typeof(BookService))
        return
            new BookService(
                new BookRepositoryImpl(this.connectionString));

    if (...)

    throw new InvalidOperationException("Unknown type.");
}

我的期望是 JAX-RS 包含一个类似的扩展模型,它可以让你练习 Pure DI。

感谢史蒂文的回答。总而言之,这是我正在执行 DI 的 JAX-RS 配置:

public class AppConfig extends ResourceConfig {

    public AppConfig() {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-unit");

        BookRepository bookRepository = new BookRepositoryImpl(emf);

        BookService bookService = new BookService(bookRepository);

        register(bookService);

  }
}