4

Found the following in the code (real names are replaced with dummy):

JAX-RS Resource

@Path("hello")
public class HelloResource {

  @Inject
  @RequestScoped
  FirstService service1;

  @Inject
  SecondService service2;

  ....

}

Dependencies

// first
public class FirstService {

  private static final Logger LOGGER = ...

  @Inject
  HttpServletRequest request;

  ....
}

// second
@ApplicationScoped
public class SecondService { .... } 

It is allowed to declare @RequestScoped on a field. But could not find anywhere how it works.

Question 1: If I specify @RequestScoped on the field which will be injected by a container, will I get the real-injected instance of request scope there?

Question 2: What if I change DI to constructor-based? Where do I put @RequestScoped in this case?

@Path("hello")
public class HelloResource {

  private final FirstService service1;
  private final SecondService service2;

  @Inject
  public HelloResource(FirstService service1, SecondService service2) {
    // set values here
  }

  ....

}
ikos23
  • 4,879
  • 10
  • 41
  • 60

2 Answers2

8

There are many, many, many things going on here. Let's try to tackle them one by one.

First, @RequestScoped is an annotation that you put on something that is being made. It is a scope annotation that tells CDI how long the thing being made should live. Trying to keep it simple, this can be a Java class:

@RequestScoped
public class Frobnicator { /* ... */ }

…or it can be a producer method:

@Produces
@RequestScoped
Frobnicator makeRequestScopedFrobnicator() { /* ... */ }

(You can put it on a field, but in that exceptionally rare case your field is now acting as a producer itself. You can read about producer fields, but except in certain Java EE scenarios they are almost always the wrong approach to take. In your case as listed above, it is certainly the wrong approach to take.)

Putting @Inject and @RequestScoped on anything doesn't make any sense.

So the answer to your first question is: no.

Your second question can also be headed off at the pass because you don't ever use scope annotations (like @RequestScoped) in injection scenarios. You always use them in production scenarios.

In other words, when you @Inject something, by definition you basically don't know what scope the thing you just injected is in; you just use it as a normal POJO and CDI takes care of giving you the right thing.

So in your case, it looks like you want this:

@Path("hello")
public class HelloResource {

  @Inject
  FirstService service1;

  @Inject
  SecondService service2;

  /* etc. */
}

…and:

// We'll talk about the lack of annotations here in a moment
public class FirstService {

  private static final Logger LOGGER = ...

  @Inject
  HttpServletRequest request;

  /* etc. */
}

This is good as far as it goes, but what scope does FirstService have? And does CDI know about it at all?

The quick answers to that question are, respectively: @Dependent (since there's no other scope annotation on it) and "probably not". This is almost assuredly not what you want.

To dig a little further, you now have to look at your META-INF/beans.xml in the archive housing FirstService. If it indicates that its bean-discovery-mode is annotated, which is highly likely, then only classes with bean-defining annotations on them will be discovered by CDI. So since FirstService doesn't have any annotations of any kind on it, most likely it won't be discovered, and CDI will blow up sometime at runtime or startup indicating that there was no dependency found for FirstService.

Let's say we put @ApplicationScoped on FirstService. This will make FirstService basically a singleton (again, keeping it simple). But wait, you say, what about the HttpServletRequest? What scope will that be in? The answer is: you, as a consumer, don't know, and you don't care. (The real answer will be of course that it will reflect the current request so in all likelihood is in request scope.) Any time you try to access that HttpServletRequest field, you'd better be in a request, or it will blow up on you.

Or you could put @RequestScoped on FirstService, in which case anything that accesses a FirstService-typed field had better be in an active request scope at the time of the access, or, again, it will all blow up on you.

Finally, you're doing all of this in the context of JAX-RS, which had its own dependency injection framework before CDI was born. To make JAX-RS and CDI play together relatively nicely required a great deal of pounding square pegs into round holes. One of those cases is that strictly speaking resource classes do not support CDI-style constructor injection, only JAX-RS-style constructor injection, which is its own (deprecated) subject. So with resource classes you typically want to stay with field injection.

Also, JAX-RS applications don't require Servlet constructs. In fact, depending on the particular combination of infrastructure you're running with, @Inject private HttpServletRequest request may not work either, and you may have to use @Context. (That is its own set of questions and answers.)

Laird Nelson
  • 15,321
  • 19
  • 73
  • 127
0

I believe @RequestScoped and the other scopes are allowed on fields (i.e. @Target({ TYPE, METHOD, FIELD })) only for specifying the scope of a producer field, i.e. they make sense only with @Produces:

@Produces
@RequestScoped
Something mySomething;
Nikos Paraskevopoulos
  • 39,514
  • 12
  • 85
  • 90