在没有 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);
}
}
我需要创建一个简单的 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);
}
}