Java:方法链接和依赖注入
使用依赖注入框架(比如)管理的服务时,使用方法链接是否可以接受 我不确定是否允许它“缓存”实例,即使它只在注入范围内 创建比萨饼的示例服务:Java:方法链接和依赖注入,java,dependency-injection,hk2,Java,Dependency Injection,Hk2,使用依赖注入框架(比如)管理的服务时,使用方法链接是否可以接受 我不确定是否允许它“缓存”实例,即使它只在注入范围内 创建比萨饼的示例服务: @Service public class PizzaService { private boolean peperoni = false; private boolean cheese = false; private boolean bacon = false; public PizzaService withPep
@Service
public class PizzaService {
private boolean peperoni = false;
private boolean cheese = false;
private boolean bacon = false;
public PizzaService withPeperoni() {
peperoni = true;
return this;
}
public PizzaService withCheese() {
cheese = true;
return this;
}
public PizzaService withBacon() {
bacon = true;
return this;
}
public Pizza bake() {
// create the instance and return it
}
}
在这里,服务被注入到JAX-RS资源中:
@Path('pizza')
public class PizzaResource {
@Inject
PizzaService pizzaService;
@GET
public Response getPizza() {
Pizza pizza = pizzaService
.withPeperoni()
.withCheese()
.bake();
return Response.ok(pizza).build();
}
}
您所做的对服务的所有其他用户都有副作用。它们都共享同一个服务实例,因此如果您使用peperoni调用
,
,它将为所有引用该服务的用户更改该布尔值
您似乎想要的是使用Builder
。也许你的服务可以实例化一个新的建设者,负责为你打造完美的比萨饼。这样可以避免所有可能的副作用:
@GET
public Response getPizza() {
Pizza pizza = pizzaService.newPizzaBuilder()
.withPeperoni()
.withCheese()
.bake();
return Response.ok(pizza).build();
}
和比萨饼制造商:
public class PizzaBuilder {
private boolean peperoni = false;
private boolean cheese = false;
private boolean bacon = false;
public PizzaBuilder withPeperoni() {
peperoni = true;
return this;
}
public PizzaBuilder withCheese() {
cheese = true;
return this;
}
public PizzaBuilder withBacon() {
bacon = true;
return this;
}
public Pizza bake() {
// create the instance and return it
}
}
和比萨饼服务:
@Service
public class PizzaService {
public PizzaBuilder newPizzaBuilder() {
return new PizzaBuilder();
}
}
这个解决方案并不完美,因为只实例化
构建器的服务没有太多用处,但是它至少可以防止您的解决方案会遇到的副作用 这取决于JAX-RS资源的范围和服务的无状态
通常,每次请求时都会创建每个JAX-RS资源实例
JSR339 3.1.1生命周期和环境
默认情况下,将为对该资源的每个请求创建一个新的资源类实例。首先是构造函数(参见
调用第3.1.2节),然后注入任何请求的依赖项(参见第3.2节),然后
方法(参见第3.3节)被调用,最后该对象可用于垃圾收集
对于以下HTTP请求
GET /pizza HTTP/1.1
创建了一个新的PizzaResource
实例,并在其中注入了一个可用的PizzaService
实例
现在,您要寻找的答案取决于容器可能维护的PizzaService
的无状态性和生命周期
希望我现在找不到规范,但是,即使PizzaService
是@无状态的
,容器也不会为不同的会话同时共享实例
我将使用生命周期侦听器方法重置服务
@Path(“/pizza”)
公共类比萨饼资源{
@施工后
私有void resetPizzaService(){//在注入后调用
pizzaService.reset();
}
@注入
私人披萨服务披萨服务;
}
其中,reset()
public void reset() {
peperoni = false;
cheese = false;
bacon = false;
}
更新
我刚刚为@Service
找到了一个很好的线程,它似乎是Spring框架的一部分。
基于@JinKwon及其评论,这是我的解决方案:
- 服务被标记,因为
@Singleton
是默认值。(谢谢@M.Deinum)
- 根据origin类的生命周期,我将通过。这在JAX-RS资源中并不是什么问题,因为它们在默认情况下已经存在了。但是其他代码(后台进程、单元测试等)可能有不同的作用域,而
提供程序
会起到不同的作用,每次都会创建单独的新实例
按照这种方法,我可以使用方法链接,返回this
。而且,这对我来说很重要,实例由DI内核管理,并且可以访问依赖性注入本身
服务:
@Service
@PerLookup
public class PizzaService {
Pizza pizza = new Pizza(); // naked pizza by default
@Inject
OvenService oven; // just to show that I can use @Inject here
public PizzaService withPeperoni() {
pizza.peperoni = true;
return this;
}
public PizzaService withCheese() {
pizza.cheese = true;
return this;
}
public PizzaService withBacon() {
pizza.bacon = true;
return this;
}
public Pizza bake() {
return oven.bake(pizza);
}
}
资源:
@Path('pizza')
public class PizzaResource {
@Inject
PizzaService pizzaService;
@GET
public Response getPizza() {
Pizza pizza = pizzaService
.withPeperoni()
.withCheese()
.bake();
return Response.ok(pizza).build();
}
}
Unittest(通过javax.inject.Provider
进行注入的示例):
@HK2
@试验
公共类比萨饼测试{
@注入
比萨服务比萨服务;
@注入
供应商pizzaServiceProvider;
public void testProviderInjection(){
披萨;
//比萨饼与作品
pizza=pizzaServiceProvider.get()
.与Peperoni()合作
.withBacon()
.奶酪
.bake();
assertTrue(比萨饼、佩佩罗尼);
assertTrue(比萨饼、培根);
assertTrue(比萨饼、奶酪);
//裸体披萨
pizza=pizzaServiceProvider.get()
.bake();
assertFalse(pizza.peperoni);
assertFalse(比萨饼、培根);
assertFalse(比萨饼、奶酪);
}
公共void testDirectInjection(){
披萨;
//比萨饼与作品
比萨饼=比萨饼服务
.与Peperoni()合作
.withBacon()
.奶酪
.bake();
assertTrue(比萨饼、佩佩罗尼);
assertTrue(比萨饼、培根);
assertTrue(比萨饼、奶酪);
//裸体披萨
比萨饼=比萨饼服务
.bake();
//这就是问题所在:比萨饼服务没有被重置,而且
//把秩序搞砸了!
assertFalse(pizza.peperoni);//将失败
assertFalse(pizza.bacon);//将失败
assertFalse(pizza.cheese);//将失败
}
}
对于构建者,PizzaService
的实例不由HK2管理,无法访问其他服务或服务定位器,对吗?我不确定我是否理解您的问题。PizzaService实例由您正在使用的任何依赖项注入框架管理。例如,在spring的例子中,该实例是唯一的,并且可以引用其他服务。如果您谈论的是构建器本身的实例,则不能使用@inject将服务注入其中。但是,在调用另一个服务的构造函数时,可以将引用传递给它。PizzaService将保留对您需要的服务的引用,例如:返回新的PizzaBuilder(其他服务)
是的,对不起,我的错-我指的是PizzaBuilder
的实例,而不是PizzaService
,当然……如果有10个请求进来会发生什么。。。仍然有一个PizzaService
@M.Deinum您的意思是PizzaService
是一个singleton?@M.Deinum无论推送多少请求,inje
@HK2
@Test
public class PizzaServiceNGTest {
@Inject
PizzaService pizzaService;
@Inject
Provider<PizzaService> pizzaServiceProvider;
public void testProviderInjection() {
Pizza pizza;
// pizza with the works
pizza = pizzaServiceProvider.get()
.withPeperoni()
.withBacon()
.withCheese()
.bake();
assertTrue(pizza.peperoni);
assertTrue(pizza.bacon);
assertTrue(pizza.cheese);
// naked pizza
pizza = pizzaServiceProvider.get()
.bake();
assertFalse(pizza.peperoni);
assertFalse(pizza.bacon);
assertFalse(pizza.cheese);
}
public void testDirectInjection() {
Pizza pizza;
// pizza with the works
pizza = pizzaService
.withPeperoni()
.withBacon()
.withCheese()
.bake();
assertTrue(pizza.peperoni);
assertTrue(pizza.bacon);
assertTrue(pizza.cheese);
// naked pizza
pizza = pizzaService
.bake();
// this is where it goes wrong: the pizzaService hasn't been reset and
// is messing up the order!
assertFalse(pizza.peperoni); // will fail
assertFalse(pizza.bacon); // will fail
assertFalse(pizza.cheese); // will fail
}
}