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

时间:2019-04-28 09:58:12

标签: dependency-injection repository jax-rs

我需要使用资源对基本的CRUD操作创建一个简单的REST API,而不使用Spring,而仅使用Java。我使用JAX-RS(Jersey实现)和Jetty作为嵌入式servlet容器。我使用JPA(Hibernate)实施)和H2内存数据库。我不使用任何DI框架,所以我使用new()“手动”完成所有DI。

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

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

2 个答案:

答案 0 :(得分:2)

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

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

但是,这不是您当前正在实践的方法。您不是将他们的依赖项注入他们的使用者。通过在BookRepositoryImpl类内创建BookService,您将应用Control Freak anti-pattern,这是Dependency Inversion Principle违规的一种特殊形式。这将BookRepositoryImpl类与BookService类紧密耦合,这可能会引起可维护性问题,因为BookRepositoryImplVolatile Dependency。易失性依赖是我们引入抽象并使用依赖注入的原因。

此外,使用静态字段只会加剧痛苦,因为在BookRepositoryImpl(或其依赖项之一)不是线程安全的情况下,这可能会引起线程安全问题。

因此,应该将BookRepositoryImpl抽象注入到BookService的构造函数中,而不是将BookRepositoryBookService紧密耦合。这样可以使两个组件保持松散耦合,并为您提供松散耦合带来的所有好处:

@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。

答案 1 :(得分:0)

感谢史蒂文的答案。总结一下,这是我执行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);

  }
}