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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在没有DI框架的情况下注入存储库相关的知识,希望对你有一定的参考价值。

我需要在资源上创建一个简单的REST API和基本的CRUD操作,不使用Spring而只使用Java.I使用JAX-RS(Jersey实现)和Jetty作为嵌入式servlet容器。我使用JPA(Hibernate实现)和H2内存数据库。我没有使用任何DI框架,因此我使用new()手动执行所有DI。

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

@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容器的有效方法很有用,但是可选工具。

然而,这不是您目前正在练习的方法。您没有将依赖项注入其使用者。通过在BookRepositoryImpl类中创建BookService,你应用Control Freak anti-pattern,是一种特殊形式的Dependency Inversion Principle违规。这使BookRepositoryImpl类与BookService类紧密结合,这可能会导致可维护性问题,因为BookRepositoryImplVolatile Dependency。易失性依赖性是我们引入抽象和使用依赖注入的原因。

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

因此,不应将BookRepositoryImplBookService紧密耦合,而应将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的经验,但是大多数框架都允许覆盖其根类的创建。例如,使用ASP.NET MVC框架,您可以实现自定义IControllerFactory,并替换框架的默认实现。在自定义工厂中,您将手工创建完整的树,使用普通的旧Java:

public IController Create(Type controllerType)
{
    if (controllerType == typeof(HomeController))
       return new HomeController(new PersonsRepositoryImpl(this.connectionString));
    if (controllerType == typeof(BooksController))
       return new BooksController(new BookRepositoryImpl(this.connectionString));
    if (...)
    throw new InvalidOperationException(controllerType.FullName + " unknown.");
}

我的期望是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);

  }
}

以上是关于在没有DI框架的情况下注入存储库的主要内容,如果未能解决你的问题,请参考以下文章

Spring框架进阶Spring V3.0 DI源码分析流程

Spring依赖注入(DI)的理解

”控制反转Ioc,依赖注入DI“如何实现的?

仅可测试性是依赖注入的理由吗?

依赖注入[4]: 创建一个简易版的DI框架[上篇]

依赖注入[4]: 创建一个简易版的DI框架[上篇]