Whenever I try to analysis to understand the spring scopes I am stuck up somewhere. Below is my understanding from my analysis and before conclude myself I would like to confirm with you. Please correct me if my understanding is wrong.
<bean id="signupController" class="com.crp.controller.SignupController"
scope="">
If scope is "request", then for every new request from client irrespective of session, the spring container will generate new instance. Once the request is completes then spring container will manage to close the life cycle of instance.
If scope is "session", then for first request of a session a new instance will be generated by spring container and maintain it for all the client request for that particular session. Once the session timed out then spring container will manage to close the life cycle of instance.
If scope is "prototype", new instance will be generated by spring container whenever the bean is requested irrespective of session. Developer should manage the life cycle of the instance because spring container will not manage life cycle of prototype scope.
If scope is "singleton", only one instance generated by spring container and maintain it to be available for all request irrespective of sessions. For every session a copy of singleton instance will be maintained so that the one session singleton object will not share by another session and spring container will manage the life cycle of the copy of singleton instance and close it when session timed out.
Note: I believe most of you may have different opinion in my understanding on singleton scope. Even I am also confused in the behaviour of singleton scope and getting different information during my analysis. Please share your thoughts
Thank you.
For singleton scope how container works for below implementation for multiple user (session) send request at same time.
Login.java:
public class Login {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
LoginController.java:
public class LoginController extends MultiActionController {
private Login login;
public ModelAndView submitLogin(HttpServletRequest request,
HttpServletResponse response) {
String userName = request.getParameter("username");
String password= request.getParameter("password");
getLogin().setUserName(userName);
getLogin().setPassword(password);
// TODO send login bean to DAO for executing further business logic.
}
public Login getLogin() {
return login;
}
public void setLogin(Login login) {
this.login = login;
}
}
context.xml:
<bean id="login" class="com.crp.bean.Login" scope="singleton">
</bean>
<bean id="loginController" class="com.crp.controller.LoginController" scope="singleton">
<property name="login" ref="login"></property>
</bean>
No, that's not entirely correct.
If scope is "request", then for every new request from client irrespective of session, the spring container will generate new instance.
No. The bean instance will only be created if the code calls a method on the bean (i.e. on the scoped proxy that wraps the actual bean instance). But you're right that every request has a bean instance that is different from the other requests, and that the bean is destroyed at the end of the request (i.e. its life-cycle hooks, if they exist, are called when the request ends).
If scope is "session", then for first request of a session a new instance will be generated by spring container and maintain it for all the client request for that particular session.
Same remark as for the request scope: the bean instance is created on-demand.
If scope is "prototype", new instance will be generated by spring container whenever the bean is requested irrespective of session.
Right. But note that you need to request a bean instance to the spring context to get a new instance. If you inject a prototype to a singleton bean, the singleton bean will keep a reference to this bean for all of its life, and the same prototype bean will thus be used every time the singleton bean calls it.
If scope is "singleton", only one instance generated by spring container and maintain it to be available for all request irrespective of sessions. For every session a copy of singleton instance will be maintained so that the one session singleton object will not share by another session and spring container will manage the life cycle of the copy of singleton instance and close it when session timed out.
No. That is completely wrong. No copy is made at all. A single instance is created, injected everywhere, and used concurrently everywhere. The bean is only destroyed when the application itself ends.
Related
It's a common best practice to renew the HTTP session when logging in a user. This will force a new session ID, avoiding session fixation vulnerabilities.
Is there a preferred pattern for implementing this with CDI when #SessionScoped beans are involved? The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.
For example, assume a session bean for storing user login information:
#Named("sessionbean")
#SessionScoped
public class SessionBean implements Serializable {
private int userId;
private String username;
private List<String> privileges;
// Accessors omitted
}
And another bean for managing the login:
#Named("loginbean")
#ViewScoped
public class LoginBean implements Serializable {
private String username;
private String password;
#Inject private SessionBean session;
#Inject private SessionManager sessionManager;
#Inject private PrivilegeManager privilegeManager;
public String doLogin() {
String destinationUrl;
if (validate(username, password)) {
FacesContext context = FacesContext.getCurrentInstance();
// force renewal of HTTP session
context.getExternalContext().invalidateSession();
// retrieve new session bean ** No longer works with CDI **
Application app = context.getApplication();
session = app.evaluateExpressionGet(context, "#{sessionbean}", SessionBean.class);
session.setUsername(username);
session.setSessionId(sessionManager.createNewSession(username));
session.setPrivileges(privilegeManager.getPrivileges(username));
destinationUrl = createLandingPageUrl();
} else {
destinationUrl = createFailureUrl("Unknown user or password");
}
return destinationUrl;
}
}
With Managed Beans this would retrieve a new SessionBean, but with CDI, the code above would just return the same SessionBean. Any recommendations or clever ideas?
The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.
Then don't invalidate the session, but change the session ID. In other words, don't use HttpSession#invalidate(), but use HttpServletRequest#changeSessionId() (new since Servlet 3.1, which you should undoubtedly already be using given that you're using JSF 2.3).
In code, replace
// force renewal of HTTP session object
context.getExternalContext().invalidateSession();
by
// force renewal of HTTP session ID
((HttpServletRequest) context.getExternalContext().getRequest()).changeSessionId();
This basically changes the JSESSIONID cookie without changing the HttpSession. It's perfect for session fixation prevention.
Explicitly invalidating the session is usually only useful during logout.
I'm going to restrict this answer to be solely about CDI since I am not a security expert. I also don't know whether the general thing being asked for is a good idea or not. Regardless, here is how I think you would do what you're asking for.
Expressed in purely CDI terms, the question can be rephrased like:
I have an object that I know came from a particular Context. I know the lifecycle of objects produced by this Context. How can I properly tell the Context to invalidate the current object that it is managing, and load or create a new one?
The general approach is going to be:
#Inject a Provider<SessionBean> instead of SessionBean directly (this will let you ask CDI for the "new" object properly)
#Inject a BeanManager (so you can get the right Context that manages SessionScoped objects)
ask the BeanManager to give you the AlterableContext corresponding to the SessionScoped annotation
tell the AlterableContext to destroy the current bean's contextual instance
call Provider.get() to cause a new one to be created
So the relevant parts of your doLogin method might look like this (untested):
final AlterableContext context = (AlterableContext) this.beanManager.getContext(SessionScoped.class);
assert context != null;
final Bean<?> bean = beanManager.resolve(beanManager.getBeans(SessionBean.class));
assert bean != null;
context.destroy(bean);
final SessionBean newSessionBean = this.sessionBeanProvider.get();
assert newSessionBean != null;
I think that should work.
My question is :
let's assume that i have a spring bean : a DAO one (it's a singleton of course)
So, when we have lot of users who want to use this bean at the same time : what happens?
or,
for every user, spring instantiate a DAO bean for him?
==> we have a singleton by application Context : did spring create a context for every user?
(it's a singleton of course)
Then it's a singleton. The ApplicationContext will only create one instance and pass that same instance around wherever it's requested.
for every user, spring [instantiate] a DAO bean for him?
No, it will retrieve and give them the same DAO instance.
==> we have a singleton by application Context : did spring create a context for every user?
I'm not exactly sure what you mean for every user. A Java application has no knowledge of users. If you mean in the context of a multithreaded application, it's still irrelevant. The injected DAO bean will still be that single instance.
As stated in the comments, it's your responsibility to handle the state of your DAO, if it's mutable, handle concurrent access. Handle transactions (possibly with #Transactional) with the datasource.
You dont use DAOs as Spring beans. Spring beans (singleton scope) are more like a service.
Lets say you have a Pizza Dao, and a PizzaService, your Service is a spring bean, while pizza isnt.
class Pizza {
Set ingredients = ... ;
public Pizza(Set s) {
...
}
private addIngredient(Object o ) {
set.add...
}
}
#Service
class PizzaService {
private Pizza createPizza(..){
Pizza p = new Pizza();
....
return pizza;
}
private void persistPizza(Pizza pizza){
..save to db..
}
}
Test your service:
class JunitFoo{
#Autowired private PizzaService service;
#Test
someMethod(){
Pizza p = service.createPizza();
p.addIngredient(cheese)
service.persistPizza(p);
}
}
You can also implement Runnable in JunitFoo and launch someMethod a lot of time with differents threads (your User), every user will get its own Pizza. But all users use the same pizzaService.
Due to this reason singleton is default scope of a spring bean. You can also create a Pizza by fetching it from your application context, but in this case, you would need prototype otherwise all users share the same pizza
<bean id="pizza" class="foo.Pizza" scope="prototype" >
<default set of pizza stuff>
</bean>
If you do it that way, your PizzaService could look like this:
#Service
class PizzaService {
#Autowired private ApplicationContext context;
private Pizza createPizza(..){
return (Pizza) context.getBean("pizza");
}
private void persistPizza(Pizza pizza){
..save to db..
}
}
This topic is way to big to cover it in a single post, i hope i could help and improve your understanding. Please also check the spring documantation about bean scopes.
I have following request scoped Spring bean with #postconstruct method init():
#Component
#Scope("request")
public class editUserBB {
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
private UserDto user;
#Autowired
private IUserService userService;
#PostConstruct
public void init() throws IOException {
String id_string = params.get("id");
Long id = Long.parseLong(id_string);
user = userService.getUserById(id);
}
public String save(){
// save to database
return "user?faces-redirect=true&id=" + (long)user.getId();
}
}
And userEdit.xhtml with h:form and commandButton:
<h:commandButton value="Save" action="#{editUserBB.save()}" />
However, after the save button is clicked the init() method is called once again annulling all the changes made to the UserDto object before I can save it to the DB. So what am I doing wrong?
And I just tested, the init() method is called even before save(), which I also don't understand..
That's not the fault of the init() method. That's just your own fault of placing the bean in the request scope instead of in the view scope.
A request scoped bean lives as long as a single HTTP request-response cycle. Opening the page with the form counts as one HTTP request. The HTTP request is garbaged when the associated HTTP response is finished sending the result to the client, including all associated request scoped beans. Submitting the form counts as another HTTP request which thus creates a completely new instance of the request scoped bean. If you explore/debug the instance's hashcode (and constructor), you'll notice that it are actually two physically completely distinct instances. There's thus absolutely no means of the init() to "override" the old values. The old values aren't there in first place.
JSF has solved this awkward behavior of a request scoped bean with introducing the view scope. You probably have ever heard/read about it before replacing JSF bean management facility by Spring's one for some reason. Spring bean management facility doesn't have a native concept of the view scope, you'd need to homegrow one.
If you intend to stick to Spring bean management facility, then your best bet is retaining the request parameter responsible for proper initialization of the data along with the form submit. You can use <f:param> for that:
<h:commandButton value="Save" action="#{editUserBB.save()}">
<f:param name="id" value="#{param.id}" />
</h:commandButton>
See also:
How to choose the right bean scope?
How do request/session/application work?
Unrelated to the concrete problem, you should avoid calling FacesContext during instance construction/initialization. This is bad design. In this particular case, move that line of obtaining the request parameter map to inside the init() method. Also here, JSF bean management facility has a standard solution in flavor of #ManagedProperty while Spring one doesn't have.
We are using Struts2-Spring integration and all my action classes implement SessionAware, sample code to action class and their spring definition is given below,
public class IRXxxxAction extends ActionSupport implements SessionAware {
private Map session;
public String execute()
{//}
public void setSession(Map<String, Object> session)
{
this.session = session;
}
}
Spring Configuration
<bean name="userAction" class="com.IRXxxxAction" >
<property name="adminDAO" ref="adminDAO" />
</bean>
If I understand correctly, each auto-wired property will be a singleton, so if the above is true, is there anyway that the session map get shared between two simultaneous requests?
Regards,
Ayush
You have asked same question on the user mailing list and as said if you are using Struts2-Spring plugin make sure to put bean scope as prototype.
Struts2 create new instance of action on each request, since action work as a model also and in order to make it thread safe a new object is being created on each request and placed on value stack.
Not proving scope will be treated by Spring as singleton and for ever request same action instance will be given back which can leads to a lot of issue from data corruption to weird behavior.
I make (thanks with some users on this portal) my application that implements SessionAware.
This is my actual code :
public class UserManager extends ActionSupport implements SessionAware {
private Map<String, Object> session;
#Override
public String execute() throws Exception {
return SUCCESS;
}
public void setSession(Map<String, Object> map) {
this.session=map;
}
public String checkLogin() {
session.put("loggedOn", true);
return SUCCESS;
}
public String checkLogout() {
session.clear();
return SUCCESS;
}
}
And i check these variables on my .jsp :
<s:if test="#session['loggedOn']!=true">
DIV LOGIN
</s:if>
<s:else>
DIV LOGOUT
</s:else>
An easy piece of code.
What i'd like to know is this :
1 - the bean is (as default) request scoped. So when the request is finished it will be destroyed. But i see that, when i put a variable in the Map, it still alive on the server. How is possible? Is a variable of my Bean.
2 - Who call the setSession method? I think the servlet, due to the fact I implements that interface?
3 - I would like to detach about saving object/bean on a Session Object. I'd like to use the Bean session scoped (as for any kind of MVC framework). How can I do it on struts2?
Hope you can make clear these questions :) Cheers
1) Your bean is a struts2 action as such it is action scoped (which is a more restrictive scope than request). I say that action is a lesser scope because you can forward an action to another action in which case the previous action goes out of scope, request scoped objects however will stay in scope until the request is served. When you implement SessionAware you are provided with a reference to the SessionObject you are then putting your object into the Session object who's life span is much longer than your action. Clear your browser cache will remove the session value... you can also remove them by code be implementing SessionAware and removing the value from the map.
2 - The session already exists. You can get the session and add keys but even if you don't put anything in it, it will be there for use.
3 - You have a later question already for this topic see my answer there.