Java: chaining method and dependency injection

Is it acceptable to use the chaining method when working with a service managed by a dependency injection infrastructure (say HK2 )

I am not sure if it is allowed to "cache" the instance, even if it is only available as part of the injection.

An example of a pizza making service:

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

Here the service is entered into the JAX-RS resource:

 @Path('pizza') public class PizzaResource { @Inject PizzaService pizzaService; @GET public Response getPizza() { Pizza pizza = pizzaService .withPeperoni() .withCheese() .bake(); return Response.ok(pizza).build(); } } 
+5
source share
3 answers

Configure the answer and its comments on top of @JinKwon, this is my solution:

  • The service is marked with @PerLookup , since @Singleton is the default. (Thanks @M. Deinum)
  • Depending on the life cycle of the origin class, I embed the service through the Provider . These are not many problems in JAX-RS resources, as they are already @RequestScoped by default. But other code (background processes, unit tests, etc.) may have a different scope, and there the Provider matters, each time creating separate fresh instances.

Following this approach, I can use the chaining method by returning this . Also, and this is important to me, the instance is managed by the DI core and has access to the dependency injection itself.

Services:

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

Resource:

 @Path('pizza') public class PizzaResource { @Inject PizzaService pizzaService; @GET public Response getPizza() { Pizza pizza = pizzaService .withPeperoni() .withCheese() .bake(); return Response.ok(pizza).build(); } } 

Unittest (injection example via 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 } } 
0
source

What you do has a side effect for all other users of the service. They all have the same service instance, so if you call withPeperoni , this will change the value of this boolean to all those who have a link to the service.

Sounds like you need to use Builder . Perhaps your service can create a new builder who will be responsible for creating the perfect pizza for you. This way you avoid all possible side effects:

 @GET public Response getPizza() { Pizza pizza = pizzaService.newPizzaBuilder() .withPeperoni() .withCheese() .bake(); return Response.ok(pizza).build(); } 

And 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 } } 

And PizzaService:

 @Service public class PizzaService { public PizzaBuilder newPizzaBuilder() { return new PizzaBuilder(); } } 

This solution is not ideal, because using the service that only creates an instance of Builder uses little, but at least prevents the side effects that you will encounter with your solution.

+1
source

It depends on the JAX-RS resource area and the idle service of the service.

Typically, each instance of a JAX-RS resource is created each time it is requested.

JSR339 3.1.1 Life Cycle and Environment

By default, a new instance of the resource class is created for each request for this resource. First, the constructor (see Section 3.1.2), then any requested dependencies are entered (see Section 3.2), then the corresponding method (see section 3.3), and finally, the object becomes available for garbage collection.

For the next HTTP request

 GET /pizza HTTP/1.1 

A new instance of PizzaResource and an available instance of PizzaService is inserted into it.

Now the answer you are looking for depends on statelessness and the PizzaService life cycle that the container can support.

Hopefully I can't find the spec now, but even if PizzaService is @Stateless , the containers will not share the instance at the same time for different sessions.

I would put a lifecycle listen method to reset the service.

 @Path("/pizza") public class PizzaResource { @PostConstruct private void resetPizzaService() { // invoked after the injection pizzaService.reset(); } @Inject private PizzaService pizzaService; } 

Where reset() will do

 public void reset() { peperoni = false; cheese = false; bacon = false; } 

Update

I found a nice thread for @Service , which seems to be part of the Spring framework. How does singlet bean serve for simultaneous request?

+1
source

All Articles