I have some serious doubts regarding scope bean dependencies because of lack of spring knowledge.
I have read reference manual at 3.5.4.5 Scoped beans as dependencies and have implemented sucessfully an example about it.
However before going further more, I wanted to share my concerns.
Let me to share my use case and little implementation details
For each user request I would like to create a city for per user.
#Configuration
public class CityFactory{
#Bean(name = {"currentCity" , "loggedInCity"})
#Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS)
#Autowired
public CityBean getCityBean(HttpServletRequest request) {
return CityUtil.findCityWithHostName(request.getServerName());
}
For each request I want to inject this city into a singleton scoped controller which is default scope for controller.
#RequestMapping("/demo")
#Controller
public class DemoController {
#Autowired
CityBean city;
#RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
public ModelAndView helloWorld(#PathVariable("name") String name, Model model) {
Map<String, Object> myModel = new HashMap<String, Object>();
model.addAttribute("hello", name);
model.addAttribute("test", "test in " + city.getDomainName() + " !!! ");
return new ModelAndView("v3/test", "m", model);
}
}
My questions:
1) Is there any race condition? I am afraid of context switches which will destroy my application in a multi request environment.
2) I am aware of another solution which creating a controller per request but it is more error prone than current solution. Because another developer can forget scoping controllers to make request.
How can I make controllers globally request scope? Just because of being little curious.
Thanks...
No race conditions - each request has its own thread
But I think there's an easier way to do what you want. You can have your CityBean:
#Service
public class CityBean {
public String getDomainName(String serverName) {
// obtain the name based on the server name
}
}
And in your controller:
#Autowired CityBean bean
pass HttpServletRequest as argument to the method, and call cityBean.getDomainName(request.getServerName());
(If you use some ORM, perhaps you'll have a City entity, which you can fetch and pass around, just beware of lazy collections)
There are no race conditions here.
That's the point of scoped proxies - the instance of CityBean injected into DemoController is a proxy which delegates calls of its method to the actual request-bound instance of CityBean, so that each request works with its own CityBean.
I agree that you shouldn't make the controller itself request-scoped - it would be confusing for other people since it's not a typical approach in Spring MVC applications.
You can also follow the approach suggest by Bozho and get rid of request-scoped bean at all, though that approach has a drawback since it requires you to add extra argument to your controller methods.
Related
Please see the example below. I wonder if there will be any difference if I specify the scope or not below. Thanks
#RestController
#RequestScope
#RequestMapping("/api/v1/user")
public class UserResource {
#GetMapping("/addresscheck")
public String getAddress() {
return customer.getAddress();
}
}
// Here does it matter I define the scope or not? is it still going to be treated as one per request?
#RestController
#RequestMapping("/api/v1/user")
public class UserResource {
#GetMapping("/addresscheck")
public String getAddress() {
return customer.getAddress();
}
}
By default, all Spring managed beans have singleton scope. So in your second implementation, only one UserResource object will be created by Spring and that will be provided every time a request for the specified URL is to be fulfilled.
However, in the first implementation, since you are annotating UserResource with #RequestScope, Spring will create a new controller object to serve each request. This means that any state information you may be maintaining in UserResource will be lost. All member variables of UserResource will also be created anew for each request.
Although I am curious as to why you would want a controller to be request scoped? Could you please share your use-case, if possible?
Here is a good article to read on the subject: Spring Bean Scopes
I'm working on Spring Boot Rest API, and I did end up using the new keyword here and there.
I'm wondering, did I do something wrong when I used the new keyword for my program. And if it is absolutely forbidden to use new keyword on a real project.
If the answer is yes should i annotate each class i wrote with #component annotation so i can instantiate an object using #autowired.
If the answer is no when can we break that rule ?
You can create objects using the new keyword in a spring application.
But these objects would be outside the scope of the Spring Application Context and hence are not spring managed.
Since these are not spring managed, any nested levels of dependency (such as your Service class having a reference to your Repository class etc)
will not be resolved.
So if you try to invoke a method in your service class, you might end up getting a NullPointer for the repository.
#Service
public class GreetingService {
#Autowired
private GreetingRepository greetingRepository;
public String greet(String userid) {
return greetingRepository.greet(userid);
}
}
#RestController
public class GreetingController {
#Autowired
private GreetingService greetingService;
#RequestMapping("/greeting")
public String greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return String.format("Hello %s", greetingService.greet(name));
}
#RequestMapping("/greeting2")
public String greeting2(#RequestParam(value = "name", defaultValue = "World") String name) {
GreetingService newGreetingService = new GreetingService();
return String.format("Hello %s", newGreetingService.greet(name));
}
}
In the above example /greeting will work but /greeting2 will fail because the nested dependencies are not resolved.
So if you want your object to be spring managed, then you have to Autowire them.
Generally speaking, for view layer pojos and custom bean configurations, you will use the new keyword.
There is no rule for using or not using new.
It's up to you if you want Spring to manage your objects or want to take care of them on your own.
Spring eases object creation, dependency management, and auto wiring; however, you can instantiate it using new if you don't want that.
I think its fine to use new keyword, but you should learn the difference between different stereotype (Controller, Service, Repository)
You can follow this question to get some clarity:
What's the difference between #Component, #Repository & #Service annotations in Spring?
Using appropriate annotation will allow you to correctly use DI (dependency injection), that will help in writing sliced tests for your spring boot application. Also the Service,Controller and Repository components are created as Singleton, so lesser GC overhead. Moreover components that you create using new keyword are not managed by Spring, and by default Spring will never inject dependencies in a object created using new.
Spring official documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
You will need new on Spring mock tests when you will have to create an object as service and inject mock object as dao.
Look at the following code; here as you see, based on a condition it's necessary to dynamically load advertisements on demand. so here you can not #autowire this group of items because all the information are loaded from DB or an external system, so you just need to fill you model accordingly.
if (customer.getType() == CustomerType.INTERNET) {
List < Advertisement > adList = new ArrayList < Advertisement > ();
for (Product product: internetProductList) {
Advertisement advertisement = new Advertisement();
advertisement.setProduct(product);
adList.add(advertisement);
}
}
Note it's appropriate to use Spring for managing external dependencies
like plugging a JDBC connection into a DAO or configurations like
specifying which database type to use.
I have searched the forum form my query but could not find a answer.
I am new to spring mvc so i am little bit confused it will be great if some one helps me out,
I have a spring mvc application, i get some data from request parameter, i have to maintain that data through out the session. how could i achieve this using Spring 3.0.3.
I had some idea in mind to implement this
1> create a pojo with session scope
2> Then in controller autowire the pojo and populate the pojo.
3> Since it is in session scope the value populated should be available throughout the session
Please let me know if i am on the right track.
Thanks.
The idea you said is one way to work with session scoped beans. You can define your session scoped POJO:
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class YourSessionBean{
...
}
and then you can inject it in your Controller classes:
#Controller
public class YourController {
#Autowired
private YourSessionBean yourSessionBean;
...
}
You can also use #SessionAttributes to store your POJO into session:
public class YourObject {
...
}
and you can use #SessionAttributes annotation in your controller to put an instance of YourObject into session:
#Controller
#SessionAttributes("yourObj")
public class YourController {
...
#RequestMapping(value="/url")
public ModelAndView process(...) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("yourObj", new YourObject()); // this will put YourObj into session
return modelAndView;
}
}
But, while using #SessionAttributes, you should consider below block of statements (copied from #SessionAttributes Doc):
NOTE: Session attributes as indicated using this annotation correspond
to a specific handler's model attributes, getting transparently stored
in a conversational session. Those attributes will be removed once the
handler indicates completion of its conversational session. Therefore,
use this facility for such conversational attributes which are
supposed to be stored in the session temporarily during the course of
a specific handler's conversation.
For permanent session attributes, e.g. a user authentication object,
use the traditional session.setAttribute method instead.
You can also use HttpSession as a method argument for your #RequestMapping handler methods and then add your POJO class to the session:
#Controller
public class YourController {
...
#RequestMapping(value="/url")
public ModelAndView process(HttpSession session,...) {
ModelAndView modelAndView = new ModelAndView();
session.setAttribute("yourObj", yourObj);
...
return modelAndView;
}
}
Say I have the following class...
#Controller
public class WebController {
#Autowired PersonService personService;
#RequestMapping(value = "/get", method = RequestMethod.GET)
#ResponseBody
#Scope("session")
public List<Player> getPerson(String personName) {
return playerService.getByName(personName);
}
}
Now this invokes the following service...
#Service("playerService")
public class PlayerServiceImpl implements PlayerService {
private List<Player> players;
#Override
#Transactional
public List<Player> getByName(final String name) {
if (players == null) {
players = getAll();
}
return getValidPlayers(name);
}
If I initially start my application, players is null, correctly, then when in the same session, I invoke this method again with a new value, players is no longer null, as you would expect. However, no new thread appears to be being created, if I open a new browser window (therefore creating a new session) and invoke this method, it still has the values from the previous session.
Why is #Scope("session") not creating a new thread in the thread pool?
I've specified <context:component-scan base-package="com." /> in my servlet-context as expected, everything works fine apart from the service methods are all acting as singletons rather than creating a new thread per session like say a Java EE container.
If players was marked as static I'd understand.
I've also tried marking my controller as #Scope("session") (as shown below) but this appears to have no impact either. What's the best way to make my Spring app create a new thread for a new session?
#Controller
#Scope("session")
public class PlayerController {
You are using #Scope annotation the wrong way.
Quoting the docs:
When used as a type-level annotation in conjunction with the Component annotation, indicates the name of a scope to use for instances of the annotated type.
When used as a method-level annotation in conjunction with the Bean annotation, indicates the name of a scope to use for the instance returned from the method.
So you can annotate either a spring component bean or a method that creates a bean if you're using java config. Java config is the only reason it even compiles (it wouldn't in pre 3.0 spring)
In your case that annotation is on a normal bean method where it doesn't mean anything.
Solving the right problem
It looks like you're trying to implement db cache by storing query results in a List<Player> players.
Don't do that. Use one of the prebuilt cache abstractions (spring has a very nice one) instead.
So where should #Scope go?
Annotating #Controller with #Scope("session") won't help as it will create session scoped controllers but the service they have injected is still a singleton.
Annotating only Service bean won't work either, cause #Controller is a singleton and it's dependencies are autowired on application startup.
Annotating both #Service and #Controller might work, but seems a bit heavy handed.
It's better to avoid state at all.
New threads are created for each request.
Your service has an instance variable (players) which is not threadsafe - it is shared by all threads. Any spring bean - including controllers and services are by default a singleton, you need to specify on the service annotation its scope.
#Service("playerService")
#Scope("session")
public class PlayerServiceImpl
But its best(simpler, easier to scale) to keep beans singletons and not rely on instance variables (unless they are also managed by spring/threadsafe/singletons).
We are working on a Spring 3.0.5 Web MVC-based application. In our code we quite often do something like this:
#ModelAttribute(ModelKeys.SOME_BEAN)
public SomeBean newSomeBean() {
return new SomeBean();
}
I think this is not necessary. But if it really wasn't, then I wonder how has this managed to slip through so many code reviews? In my understanding, if a controller method wanted a NEW SomeBean, then annotating a parameter in that method with #ModelAttribute should be enough? Spring would then use the default constructor to new up the required bean for invoking the controller method, e.g.:
#RequestMapping(method = RequestMethod.POST)
public String doIt(
#ModelAttribute(ModelKeys.SOME_BEAN) final SomeBean bean,
final BindingResult bindingResult)
{
...
}
here, Spring would new up an instance of SomeBean and try to data-bind into it from the POSTed data, right? There's no need for the method as shown in the first code snippet? Please can you confirm this or provide me with your thoughts on this? Would I be introducing a risk if I just went ahead and removed all these methods that do nothing other than new up an empty bean?
#ModelAttribute annotation on a method does not bind the bean attributes with HTTP request parameters. This is the key difference from the same annotation on a method parameter. Such approach is useful to populate some data that does not depend on request parameters in the model, for instance, values of a comboboxes taken from dictionaries. This is especially helpful, if you have several handler methods in a controller, e.g. to view/change/delete the same type of object and you need the same set of model attributes in all of them.