java spring #component scope and HttpServletRequest - java

One of my classes deals with HttpServletRequest and is a component like this:
#Component
public class AuthorizationService {
#Autowired
HttpServletRequest request;
public Boolean authorize(Integer fetId) {
...play with headers, db and other stuff...
}
and is used somewhere else like this
public class CarRestController {
#Autowired
CarService service;
#Autowired
AuthorizationService authorizer;
#RequestMapping(method = RequestMethod.GET)
public List<Car> get()throws Exception {
authorizer.authorize(666);
...
return cars;
}
My worry is that since AuthorizationService is a #component, it will be a singleton by default and therefore there can only be one request that will be swapped by newer ones coming as it is processed.
Should I do this to solve the problem?
#Component
#Scope("prototype")
public class AuthorizationService {
Many Thanks

Remove the #Scope on the service and don't worry, your controller is also a singleton (because it's managed by spring).
BTW: you are missing a #Controller on your controller
Some reading:
Spring framework reference: The IoC container
Must Spring MVC Classes be Thread-Safe

Spring takes care of request scoped object like HttpServletRequest automatically. Remove #Scope and you should be fine.

Related

Why I can replace #RestContoller with #Component and it still works?

Can someone explain why replacement of RestController annotation with Component has no any visible effect in my case?
My controller:
#RestController
#RequestMapping("/api/employees")
public class EmployeeController {
#Autowired
EmployeeService employeeService;
#PostMapping("")
public Employee saveEmployee(#Valid #RequestBody Employee employee) {
return employeeService.save(employee);
}
...
This works in the same way:
#Component
#ResponseBody
#RequestMapping("/api/employees")
public class EmployeeController {
...
The not-in-depth description of individual annotations would be:
#RequestMapping registers your class for servlet mapping.
#Component registers your class for dependency injection.
#ResponseBody will add the return value of the method to the HTTP response body.
1st case - #RestController registers your class for DI and adds #ResponseBody annotation to it, #RequestMapping registers class for servlet mapping.
2nd case - #Component registers your class for DI and you added a manual #ResponseBody annotation to it, #RequestMapping registers class for servlet mapping.
Both of the cases above do the same thing, so that's why it 'just works'.

Creation of prototype scoped component in Spring request handler method

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

How to inject spring bean into ContainerRequestFilter using AutoWired?

I'm using RESTEasy 3 and Spring 4 and I'm trying to inject #Autowired an service bean into my interceptor as follow below:
But running this code it's returning Null Pointer Exception when access my access service:
#Provider
#MyAnnotationToIntercept
public class MyInterceptor implements ContainerRequestFilter {
private MyAccessService accessService;
#Autowired
public MyInterceptor(MyAccessService accessService) {
this.accessService = accessService;
}
public MyInterceptor() {
}
#Override
public void filter(ContainerRequestContext requestContext) {
// DO SOME STUFF Using accessService
}
}
#Component
public class MyAccessService {
private MyDep1 dep1;
#Autowired
public MyAccessService(Mydep1 dep1) {
this.dep1= dep1;
}
}
Is there any way to achieve this? It's really possible?
You will need to use WebApplicationContextUtils's method to get a bean inside filter which is not managed by spring. Here is the example
MyAccessService myAccessService = (MyAccessService) WebApplicationContextUtils.getRequiredWebApplicationContext(httpServletRequest .getServletContext()).getBean(MyAccessService.class);
And to get HttpServletRequest instance you can use #context injection
#Context
private HttpServletRequest httpServletRequest ;
Looks like you have placed #Autowired annotation at the wrong place. It should be above the declaration of accessService. And depending on how you have configured application context, you may/may not need a setter method for accessService instance variable.

Instantiating a #autowired bean inside #postconstruct method inside controller, spring

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());
}

Problems with Injection (Using vaadin)

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.

Categories

Resources