I want to call a common service for all the controllers to get a commmon ModelAndView object with some common objects inside it.
So I created a superclass for all the controllers- BaseController, and I am initiating the common model object inside the constructor of BaseController by calling the a method, initCommonData which uses a #Autowired bean CommonDataService, which is not present at the construction time of object and returns null, so what I should do to get #autowired dependency inside constructor.
FYI - I am using this common servie and common data to get some commod data which will be used on each jsp in header and footer of the site.
So if there is some another way of doing this without calling the common service in each controller method, in each controller please suggest.
Here is my code -
BaseController
#Controller
public class BaseController {
#Autowired
private CommonDataService commonDataService;
protected ModelAndView model;
public BaseController() {
this.initCommonData();
}
public void initCommonData(){
this.model = new ModelAndView();
this.model.addObject("headerData",commonDataService.getHeaderData());
this.model.addObject("footerData",commonDataService.getFooterData());
}
subclass controller -
#Controller
public class HomeController extends BaseController {
#Autowired
CategoryService categoryService;
#Autowired
CompanyService companyService;
#RequestMapping(value = { "", "/", "home" })
public ModelAndView homePage() {
model.setViewName("home");
.
.
.
model.addObject("name", value);
model.addObject("name2", value2);
return model;
}
CommonServiceClass -
#Service
public class CommonDataService {
#Autowired
CompanyService companyService;
#Autowired
CategoryService categoryService;
#Cacheable
public List<Category> getHeaderData(){
return categoryService.getTopCategoryList();
}
#Cacheable
public List<Company> getFooterData(){
return companyService.getTopCompanyList();
}
Please suggest if there is any other good way of doing this, getting common data from server to jsp.
Whatever #Andreas has suggested is the best solution i.e., mark your BaseController as abstract and use #Postconstruct. This makes perfect scense because BaseController itself does not own any url mappings in your case, so do not mark it as #Controller
Because of any reason, if you are looking for other options, you can consider marking your BaseController as #Component and use #Postconstruct for initCommonData so that this method will be called automatically once the BaseController bean has been loaded by the spring container:
#Component
public class BaseController {
#Autowired
private CommonDataService commonDataService;
protected ModelAndView model;
#Postconstruct
public void initCommonData(){
this.model = new ModelAndView();
this.model.addObject("headerData",commonDataService.getHeaderData());
this.model.addObject("footerData",commonDataService.getFooterData());
}
}
First, remove #Controller from your base class. You might even make the class abstract to help indicate/document that it must be subclassed.
Next, don't call initCommonData() from the constructor. Spring cannot inject field values until after the object is created, so there is no way for Spring to wire in commonDataService before constructor completes.
Instead, annotate initCommonData() with #PostConstruct:
public class BaseController {
#Autowired
private CommonDataService commonDataService;
protected ModelAndView model;
#PostConstruct
public void initCommonData(){
this.model = new ModelAndView();
this.model.addObject("headerData",commonDataService.getHeaderData());
this.model.addObject("footerData",commonDataService.getFooterData());
}
Related
I'm writing a Spring app and learning Spring as I go. So far, whenever I find myself wanting to give something a reference to the ApplicationContext, it has meant I'm trying to do something the wrong way so I thought I'd ask before I did it.
I need to instantiate a prototype bean for each request:
#Component
#Scope("prototype")
class ComplexThing {
#Autowired SomeDependency a
#Autowired SomeOtherDependency b
public ComplexThing() { }
// ... complex behaviour ...
}
So I tried this:
#Controller
#RequestMapping ("/")
class MyController {
#GetMapping
public String index (ComplexThing complexThing, Model model) {
model.addAttribute("thing", complexThing);
return "index"
}
}
And I expected Spring to inject a new ComplexThing for the request, just like it injected a Model. But then I found the correct interpretation of that is that the caller is going to send a ComplexThing in the request.
I thought there would be a way of injecting Beans into request handlers, but I don't see one here.
So in this case am I supposed to make my Controller ApplicationContextAware and getBean?
I solved it with the ObjectProvider interface:
#Controller
#RequestMapping ("/")
class MyController {
#Autowired
ObjectProvider<ComplexThing> complexThingProvider;
#GetMapping
public String index (Model model) {
model.addAttribute("thing", complexThingProvider.getObject());
return "index"
}
}
The other benefit of ObjectProvider is that was able to pass some arguments to the constructor, which meant I could mark some fields as final.
#Controller
#RequestMapping ("/")
class MyController {
#Autowired
ObjectProvider<ComplexThing> complexThingProvider;
#GetMapping
public String index (String username, Model model) {
model.addAttribute("thing", complexThingProvider.getObject(username));
return "index"
}
}
#Component
#Scope("prototype")
class ComplexThing {
#Autowired SomeDependency a
#Autowired SomeOtherDependency b
final String username;
public ComplexThing(String username) {
this.username = username;
}
// ... complex behaviour ...
}
Your point is correct. There is no way, the prototype scoped beans (actually every other bean type too) can be directly injected into a controller request handler. We have only 4 options.
Get application Context in the caller, and pass the bean while calling the method. (But in this case, since this is a request handler, this way is not possible).
Making the controller class ApplicationContextAware, set the applicationContext object by overriding setApplicationContext() method and use it to get the bean's instance.
Create a private variable of the bean type and annotate it with #Autowired.
Create a private variable of the bean type and annotate it with #Inject (#Autowired and #Inject have the same functionality. But #Autowired is spring specific).
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
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).
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.
i'm using Spring MVC for my project. Is it possible to use the same autowired object on another class?
I have two controllers, one for navigation and one for the operations.
this is my sample navigation controller:
#Controller
public class NavigationController {
#Autowired
private DepartmentDAO deptDao;
#RequestMapping("/")
public ModelAndView panel() {
ModelAndView mv = new ModelAndView("panel");
Department dept = deptDao.getDept();
mv.addObject("department",dept);
return mv;
}
}
then my operations controller:
#Controller
public class OperationController {
#RequestMapping(value = "/save.do", method = RequestMethod.POST)
public ModelAndView saveEvent(){
ModelAndView mv = new ModelAndView("next");
Event event = new EventCreator().createEvent();
//some logic here
return mv;
}
}
And this is my business delegate class :
public class EventCreator {
public Event createEvent(){
//logic here
//I need to use the deptDao here.
}
}
Thank You So Much!
You can simply Autowire DepartmentDAO in the EventCreator class, like how your autowired it in NavigationController class. Make sure that you annotate the EventCreator class with #Component and include it in the package where component scanning is done so that spring will autowire the DepartmentDao in your EventCreator class.
#Component
public class EventCreator {
#Autowired
private DepartmentDAO deptDao;
public Event createEvent(){
//logic here
//I need to use the deptDao here.
//deptDao.getAllDepartments();
}
}
You can #Autowired a spring bean object in another spring bean object
Assumption
I assume that you have declared DepartmentDAO with #Repository
annotation as you haven't include code of DepartmentDAO in your
question
There are two ways to solve your problem ,
one is very well explain by #TimeTravel annotate EventCreator with
#Component with create spring bean and you can easy autowire
DepartmentDAO in the EventCreator class
As You have two controller class which makes it spring beans as they are annotated with #Controller, what you can do you can Autowire
departmentDAO in OperationController class and pass instance of
DepartmentDAO as argument in EventCreator class constructor
#Controller
public class OperationController {
#Autowired
private DepartmentDAO deptDao;
#RequestMapping(value = "/save.do", method = RequestMethod.POST)
public ModelAndView saveEvent(){
ModelAndView mv = new ModelAndView("next");
Event event = new EventCreator(deptDao).createEvent();
//some logic here
return mv;
}
}
public class EventCreator {
private DepartmentDAO deptDao=null;
public EventCreator(DepartmentDAO deptDaoAsArg){
deptDao=deptDaoAsArg;
}
public Event createEvent(){
//logic here
//I need to use the deptDao here.
}
}
I am newbie with EJB and Injections...
I am currently using Vaadin framework with CDI
I have been trying to using injection but i have not could do it...
In my Vaadin UI class MyVaadinUI i have tried...
CDIUI("")
#SuppressWarnings("serial")
public class MyVaadinUI extends UI {
#EJB
UserController userController;
#Override
protected void init(VaadinRequest request) {
System.err.println("desde controller "+userController.getAll().size());
}
}
UserController
#Stateless
public class UserController {
#EJB
IUserDAO userDao;
public List<User> getAll() {
return userDao.findAll();
}
}
and it works!!
but when I do not inject UserController, it does not work... In other words when I instance the class UserController the injection in this class does not work...
Code does not work
CDIUI("")
#SuppressWarnings("serial")
public class MyVaadinUI extends UI {
#Override
protected void init(VaadinRequest request) {
UserController userController = new UserController();
System.err.println("desde controller "+userController.getAll().size());
}
}
Somebody can explain me why?
Thanks
Nicolas
Only in injected objects will have its dependencies injected. If you create an object with new all field having #inject, #ejb or #resource will not be injected.
In your case you create UserController like this:
UserController userController = new UserController();
and so this field will not be injected:
#EJB
IUserDAO userDao;
And therefore getAll() will throw a NullPointerException.
I use vaadin and cdi for projects. I'd recommend to use injection for almost everything or not at all. I inject my uis, views, own components... (and do not create them with new) so it is possible to inject ejb beans or other things into them. If you are using it only sometimes you are ending up with am mixture of injection and normal object creation and will have to pass around injected objects to other object you instantiated yourself. In another project of mine this happened and got really problematic for future changed in the code.
I want to have one controller class, but 4 instances of it, each of instance will have own datasource and controller path, everything else (methods, validations rules, views names) will be the same;
So i need something like this :
class MyController{
private MyService service;
#RequestMapping("somework")
public String handleRequest(){
........
}
....................
}
Configuration class :
#Configuration
#EnableWebMvc
public class AppConfiguration {
#Controller // assuming it exists to get the
#RequestMapping('con1') // desired result
MyController controller1(){
MyController con = new MyController();
con.setService(service1Bean);
return con;
}
#Controller // assuming it exists to get the
#RequestMapping('con2') // desired result
MyController controller2(){
MyController con = new MyController();
con.setService(service2Bean);
return con;
}
...............................
}
No, you can't do this.
First, annotations are a set in stone at compile time. They are constant meta data that you cannot modify. So even though, they are accessible at run time through reflection, you cannot modify them.
Second, the #Controller annotation call only be used to annotate types. You cannot use it on a method. There is no corresponding annotation in Spring MVC that does what you want in your example. (You could always write your own.)
Finally, the Spring MVC stack registers your #Controller beans' methods as handlers mapping them to the various URL patterns you provide. If it tries to register a pattern that has already been registered, it fails because duplicate mappings are not allowed.
Consider refactoring. Create a #Controller class for each path you want but move the logic to a #Service bean which you can customize to use whatever data source you need.
You may achieve what you want by implementing an abstract superclass of
your controller, with constructor parameters for your service.
Then you should write derive your controllers from the abstract superclass,
with a constructor, where you inject your concrete service implementation:
public abstract class MyBaseController {
private MyService service;
public MyBaseController(final MyService service) {
this.service = service;
}
...
#RequestMapping("method1")
public ... method1( ... ) {
...
}
}
#Controller
#RequestMapping("con1")
public MyController1 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con1") final MyService service) {
super(service);
}
}
#Controller
#RequestMapping("con2")
public MyController2 extends MyBaseController {
#Autowired
public MyController1(#Qualifier("con2") final MyService service) {
super(service);
}
}
#Configuration
public class MyConfiguration {
#Bean(name = "con1")
public MyService serviceCon1() {
return ...;
}
#Bean(name = "con2")
public MyService serviceCon2() {
return ...;
}
}