简体   繁体   中英

Inject @RequestScoped inside @Stateless

I am trying to inject a client depended object inside a statless object. I did manage to do it, but I am not really sure how and why it works because I don't fully understand JavaEE dependency injection. I have 3 relevant classes

  • BookmarkRepository - fetching the data from the db
  • RequestData - all the relevant info for the user request that I might need
  • BookmarkEndpoint - REST endpoint

import java.util.UUID;
import javax.enterprise.context.RequestScoped;

@RequestScoped
public class RequestData {

    private final UUID correlationId;

    public RequestData() {
        correlationId = UUID.randomUUID();
    }

    public UUID getCorrelationId() {
        return correlationId;
    }
}


import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class BookmarkRepository {

    @PersistenceContext
    private EntityManager em;

    @Inject
    RequestData requestData;

    @Override
    public String test() {
        StringBuilder sb = new StringBuilder();
        String objectid = Integer.toString(System.identityHashCode(this));
        String correlationId = requestData.getCorrelationId().toString();

        sb.append(correlationId);
        sb.append("    OBJECT ID: " + objectid);
        return sb.toString();
    }
}
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("bookmarks")
public class BookmarkEndpoint {

    @Inject
    private BookmarkRepository bookmarkRepository;

    @Inject
    private RequestData requestData;

   
    @GET
    @Path("/test")
    @Produces(MediaType.TEXT_PLAIN)
    public Response testEndpoint() {
        String outerCorelationId = requestData.getCorrelationId().toString();
        sb.append(outerCorelationId);

        String innerCorelationId1 = bookmarkRepository.test();
        sb.append("\n" + innerCorelationId1);

        String innerCorelationId2 = bookmarkRepository.test();
        sb.append("\n" + innerCorelationId2);

        return ResponseBuilder.createOkResponse(Response.Status.OK, finalMessage);
    }
}

Output when I call the program with 2 HTTP calls

FIRST HTTP CALL
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5    OBJECT ID: 717717270
ba4ac8b9-8e3b-4632-9c5e-89fe9b6f2cc5    OBJECT ID: 717717270

SECOND HTTP CALL
9f563047-3d8f-48f5-849d-9a0bf1df46ae
9f563047-3d8f-48f5-849d-9a0bf1df46ae    OBJECT ID: 717717270
9f563047-3d8f-48f5-849d-9a0bf1df46ae    OBJECT ID: 717717270

My questions:

  • Does @Inject trigger each time a method from BookmarkEndpoint is called? I though @Inject is only triggered when a object is first being created, but as we can see in the output it seems that we are dealing with the same object and each time it's method is called it is being injected with a new RequestData object
  • Is it possible that two HTTP calls get the same @Statless object during the same time and then the RequestData of one client will be squashed because of the other client RequestData
  • When is @Statless created and when it is destoyed? I have seen many topic where it is said "when it is needed it is created" and "when it is not needed it is destroyed". What does that really mean. If we trust my output then my @Statless object is always in memory
  • Should BookmarkRepository really be @Statless because I obviously have a state that is unique to each client

On very important point your questions are missing:

CDI Injects a proxy , which is like a router. It does not inject the actual object. If you put a breakpoint in getCorrelationId() and take a look at the stack trace, you'll see there's a stack frame where a CDI Proxy is called at some point.

Why is this important?

This allows for the creating programs where a RequestScoped bean can be injected with a SessionScoped bean, and vice-versa. The container routes you to the correct instance of the class when the function on the the proxy is called. It literally looks it up and sends the function call to the right place!

So to get to your questions:

Does @Inject trigger each time a method from BookmarkEndpoint is called?

Neither. @Inject is just an annotation. BookmarkEndpoint is injected with a proxy of BookmarkRepository that is a sublcass of BookmarkRepository , and the CDI Framework defines this subclass at runtime. BookmarkRepository is a stateless EJB, so it will have a pool of instances. The proxy will pick an instance out of the pool and invoke the method on it. Since you don't have a CDI scope on BookmarkEndpoint , I believe it will be re-created every request (don't quote me on that, use the debugger). We generally slap @ApplicationScoped on all of our JAX-RS beans as an optimization.

Is it possible that two HTTP calls get the same @Statless object during the same time and then the RequestData of one client will be squashed because of the other client RequestData

No. Instead, Stateless beans are pooled. If there is a bean available in the pool, two requests will be handled at once. If there is no bean available, the request blocks until an instance becomes available. Pool sizes and timeouts are tunable and are a container specific setting.

When is @Statless created and when it is destoyed?

Short story: The pool of beans is created at boot time and destroyed at shutdown, unless you throw an exception out of the bean, than that instance is destroyed. See here for the full explanation: https://docs.oracle.com/javaee/6/tutorial/doc/giplj.html

Should BookmarkRepository really be @Statless

@Stateless will absolutely work, but it's not necessary: you don't really need a pool for anything. @Singleton @Lock(LockType.Read) would be more appropriate. What would be best is @ApplicationScoped @Transactional(TxType.Required) since that avoids EJBs altogether

because I obviously have a state that is unique to each client

You do... but not in the way you're thinking: Remember the following is a proxy:

    @Inject
    RequestData requestData;

Its going to route you to an instance of the bean that is bound to the current request. Your "state" is being held by the container.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM