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
    }

}