Maikkeyy
Maikkeyy

Reputation: 1047

CDI - @Injected field null

I want to initialize a collection and fill it with data at the startup of my application. Then I would like to access it everywhere in my application. I want for example that my REST API can access the shared collection that is already populated with data.

I first tried to do it with an startup class annotated with @Startup and @Singleton. When I injected my userService in there I had some problems and because of the advice in this post: @Inject and @PostConstruct not working in singleton pattern I tried to do it with an @ApplicationScoped annotation:

@Named
@ApplicationScoped
public class KwetterApp {

    @Inject
    private UserService service;

    @PostConstruct
    public void init() {
        try {
            User harry = new User("Harry", "[email protected]", "New York", "http://harry.com", "Hi, I'm Harry!", UserType.REGULAR);
            User nick = new User("Nick", "[email protected]", "California", "http://nick.com", "Hi, I'm Nick!", UserType.REGULAR);
            User jane = new User("Jane", "[email protected]", "Texas", "http://jane.com", "Hi, I'm Jane!", UserType.REGULAR);

            Tweet tweet = new Tweet("eating...", harry);
            Tweet tweet1 = new Tweet("swimming...", harry);
            Tweet tweet2 = new Tweet("jogging...", jane);

            harry.addTweet(tweet);
            harry.addTweet(tweet1);
            jane.addTweet(tweet2);
            service.create(harry);
            service.create(nick);
            service.create(jane);
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

    public UserService getService() {
        return service;
    }
}

I inject this class in my rest service:

@RequestScoped
@Path("/user")
public class UserRest {

//    @Inject
//    private UserService userService;

@Inject
private KwetterApp kwetterApp;

//    private KwetterApp kwetterApp = KwetterApp.getInstance();
private UserService userService = kwetterApp.getService();

@GET
@Produces({"application/json"})
public List<User> get() throws UserException {

    return userService.getAll();
    }
}

When injecting this KwetterApp it leads to the following exception:

StandardWrapperValve[rest.RestApplication]: Servlet.service() for servlet rest.RestApplication threw exception
java.lang.NullPointerException
    at rest.UserRest.<init>(UserRest.java:27)
    at rest.UserRest$Proxy$_$$_WeldClientProxy.<init>(Unknown Source)

I have an empty beans.xml file with bean-discovery-mode set to 'all'. The CDI framework should recognize my KwetterApp class for injection, right? Why is it null?

Thanks in advance,

Mike

Upvotes: 0

Views: 3488

Answers (3)

Vikrant Korde
Vikrant Korde

Reputation: 419

We faced similar issue and found that the class whose member variables were giving null pointer exception was creating a new instance with "new" keyword. After removed its "new" instance creation and replaced it with @Inject, it started working. We were using Quarkus.

In the context of this question, there may be a statement like below

UserRest userRest = new UserRest();

Remove this statement and use below statement

@Inject
UserRest userRest;

This should solve the problem.

More detailed answer is available about Autowired which is same as Inject. Why is my Spring @Autowired field null?

Upvotes: 1

Thomas Herzog
Thomas Herzog

Reputation: 506

If you want to initialize an object by using an injected bean, then you have to use a @PostConstruct annotated method, because injected CDI beans are only available in CDI in a @PostContruct annotated method and afterwards and not during field initialization or the constructor invocation.

Therefore that the UserService is a CDI bean already, you can just inject it into your rest service bean, because it will be the same bean used within the current active scope. KwetterApp is available within the current active scope, so UserService will be as well. Only @Dependend scoped beans behave different, whereby each bean gets its own instance provided.

Upvotes: 0

LppEdd
LppEdd

Reputation: 21172

Here

@Inject
private KwetterApp kwetterApp;
private UserService userService = kwetterApp.getService();

I do not think the kwetterApp field is going to be set before userService.
CDI will set that field after the object has been constructed.

An alternative, which should be used anyway, is constructor injection

@RequestScoped
@Path("/user")
public class UserRest {
  private KwetterApp kwetterApp;
  private UserService userService;

  protected UserRest() {}

  @Inject
  public UserRest(final KwetterApp kwetterApp) {
    this.kwetterApp = kwetterApp;
    this.userService = kwetterApp.getService();
  }

  @GET
  @Produces({"application/json"})
  @Override
  public List<User> get() throws UserException {
    return userService.getAll();
  }
}

A protected constructor is needed because @RequestScoped is a normal-scoped bean, and it must be proxiable, as described in the specification.

The only annotation that doesn't require an empty constructor is @Singleton (from javax.inject).

Upvotes: 2

Related Questions