javacdi

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", "harry@outlook.com", "New York", "http://harry.com", "Hi, I'm Harry!", UserType.REGULAR);
            User nick = new User("Nick", "nick@outlook.com", "California", "http://nick.com", "Hi, I'm Nick!", UserType.REGULAR);
            User jane = new User("Jane", "jane@outlook.com", "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


Solution

  • 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).