Reputation: 1047
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
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
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
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