在使用由依赖注入框架管理的服务(比如HK2)时,使用方法链是否可以接受?
我不确定是否允许"缓存"实例,即使它只在注射范围内。
创建披萨的示例服务:
@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();
}
}
答案 0 :(得分:1)
您正在做的事情对该服务的所有其他用户都有副作用。它们都共享相同的服务实例,因此如果您调用withPeperoni
,它将为所有引用该服务的人更改该布尔值。
您似乎想要使用Builder
。也许您的服务可以实例化一个新的构建器,该构建器将负责为您构建完美的披萨。这样你可以避免所有可能的副作用:
@GET
public Response getPizza() {
Pizza pizza = pizzaService.newPizzaBuilder()
.withPeperoni()
.withCheese()
.bake();
return Response.ok(pizza).build();
}
和PizzaBuilder:
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
}
}
和PizzaService:
@Service
public class PizzaService {
public PizzaBuilder newPizzaBuilder() {
return new PizzaBuilder();
}
}
此解决方案并不完美,因为没有太多使用仅实例化Builder
的服务,但它至少可以防止您在解决方案中遇到的副作用。
答案 1 :(得分:1)
这取决于JAX-RS资源的范围和服务的无状态。
通常,每次请求时都会创建每个JAX-RS资源实例。
默认情况下,为每个对该资源的请求创建一个新的资源类实例。首先是构造函数(参见 调用3.1.2节,然后注入任何请求的依赖项(参见第3.2节),然后是相应的 调用方法(参见第3.3节),最后该对象可用于垃圾收集。
对于以下HTTP请求,
GET /pizza HTTP/1.1
创建了PizzaResource
的新实例,并在其中注入了PizzaService
的可用实例。
现在您正在寻找的答案取决于容器可能维护的PizzaService
的无状态和生命周期。
希望我现在找不到规范,但即使PizzaService
是@Stateless
,容器也不会同时为不同的会话共享实例。
我会使用生命周期监听器方法来重置服务。
@Path("/pizza")
public class PizzaResource {
@PostConstruct
private void resetPizzaService() { // invoked after the injection
pizzaService.reset();
}
@Inject
private PizzaService pizzaService;
}
reset()
将在哪里
public void reset() {
peperoni = false;
cheese = false;
bacon = false;
}
我刚刚为@Service
找到了一个很好的线程,它似乎是Spring框架的一部分。
How does the singleton Bean serve the concurrent request?
答案 2 :(得分:0)
建立在@ JinKwon的answer及其评论之上,这是我的解决方案:
@PerLookup
,因为@Singleton
是默认设置。 (谢谢@M.Deinum)@RequestScoped
。但是其他代码(后台进程,单元测试等)可能有不同的范围,Provider
会产生差异,每次都会创建单独的新实例。按照这种方法,我可以使用方法链,返回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
@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
}
}