12

Is there any scope like JSF @ViewScoped in Spring 3.0? I have an application using JSF+Spring where backing beans are managed by Spring. I didn't find any scope like JSF wiew scope in Spring. I saw the blog Porting JSF 2.0’s ViewScope to Spring 3.0, but it didn't work for me.

Here's my attempt on the custom Spring scope:

import java.util.Map;

import javax.faces.context.FacesContext;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

/**
 * Implements the JSF View Scope for use by Spring. This class is registered as a Spring bean with the CustomScopeConfigurer.
*/
public class ViewScope implements Scope {

    public Object get(String name, ObjectFactory<?> objectFactory) {

        System.out.println("**************************************************");
        System.out.println("-------------------- Getting objects For View Scope ----------");
        System.out.println("**************************************************");
        if (FacesContext.getCurrentInstance().getViewRoot() != null) {
            Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
            if (viewMap.containsKey(name)) {
                return viewMap.get(name);
            } else {
                Object object = objectFactory.getObject();
                viewMap.put(name, object);
                return object;
            }
        } else {
            return null;
        }
    }

    public Object remove(String name) {
        System.out.println("**************************************************");
        System.out.println("-------------------- View Scope object Removed ----------");
        System.out.println("**************************************************");

        if (FacesContext.getCurrentInstance().getViewRoot() != null) {
            return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
        } else {
            return null;
        }
    }

    public void registerDestructionCallback(String name, Runnable callback) {
        // Do nothing
    }

    public Object resolveContextualObject(String key) {         return null;
    }

    public String getConversationId() {
        return null;
    }

}

application-context.xml:

<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
    <property name="scopes">
            <map>
                <entry key="view">
                    <bean class="com.delta.beans.ViewScope"/>
                </entry>
            </map>
        </property>
 </bean>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
khan
  • 2,664
  • 8
  • 38
  • 64
  • 1
    This post of mine may help : http://stackoverflow.com/q/12884822/1055089 – Vrushank Oct 22 '12 at 05:44
  • Yea But the implementation is not working in my code – khan Oct 22 '12 at 07:25
  • could you please post the code?? I used the same in my app and it worked. I am using JSF2 + Spring 3 as well... – Vrushank Oct 22 '12 at 07:29
  • yes I am using the same JSF2 + Spring 3 ... I have pasted .. – khan Oct 22 '12 at 07:43
  • Can you elaborate as to what is not working? Just to make sure does your code work with @Scope("request")? – Ravi Kadaboina Oct 22 '12 at 18:34
  • As i stated earlier that @Scope("request") is making problem in my scenario as when request send for the second drop down then previous value which is being used by 2nd drop has dropped – khan Oct 23 '12 at 03:29
  • See this link: [View scope Spring](http://www.esseconhece.com.br/programacao/java/disponibilizando-viewscope-para-spring/) worked for me... – Ednilson Campos Mar 06 '13 at 23:48
  • Related: [How to inject a Spring component/service in JSF managed bean?](http://stackoverflow.com/q/18387993) – BalusC Jun 15 '16 at 18:55

5 Answers5

5

Recently I've created maven artifact which will solve this problem.

See my github javaplugs/spring-jsf repository.

rumatoest
  • 403
  • 5
  • 9
3

I did something like this without Porting bean to Spring. It's working for me.

@ManagedBean(name="bean")
@ViewScoped  // actual jsf viewscoped only with javax.faces.viewscoped import
public class Bean implements
Serializable {


@ManagedProperty(value="#{appService}")   // Spring Manged Bean and singleton
private transient AppService appService;

  // Getting AppService Object which is singleton in the application during deserialization 
 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
          stream.defaultReadObject();
          FacesContext context = FacesContext.getCurrentInstance();
          appService = (AppService)context.getApplication()
                .evaluateExpressionGet(context, "#{appService}", AppService.class);
       }
}
SRy
  • 2,901
  • 8
  • 36
  • 57
  • That's a pure JSF solution. It works fine, but it lacks the features of Spring. For instance, it doesn't allow for aspect oriented programming. – Stephan Rauh Apr 29 '14 at 15:28
  • I am not very sure about `Spring AOP`. Becuase I tested this according to my requirement at that time. But I had `Spring core` which perfectly worked for me. – SRy Apr 29 '14 at 15:32
  • Ah, I see. Most likely you converted your JSF solution to a Spring solution by adding the SpringBeanFacesELResolver to faces.config. If so, AOP should work fine. – Stephan Rauh Apr 29 '14 at 21:18
  • 1
    Nonetheless it's not a full-blown view scoped Spring bean. You commented it yourself: the class `Bean` is view-scoped, but the spring bean it uses belongs to a different scope. According to the comment, it's a singleton, and you called it a service, so it's probably stateless. Most developers are content with your solution. But it's not the same: The Spring bean mustn't store state specific to the view. – Stephan Rauh Apr 29 '14 at 21:26
  • I am not sure where it's storing the state related to view. I am just getting the spring bean which goes null at the time of Serialization cause it's marked as transient because `@viewscope` wants all of dependent beans to be `Serialized`. And one more thing `Singletons` are not stateless. – SRy Apr 29 '14 at 22:19
  • If `singletons` are `stateless` What is the purpose of implementing a bean as singleton when it returns a new instance everytime. – SRy Apr 29 '14 at 22:45
  • True. I was referring to view state. Singletons can hold a single global state. The view scopes can't store their state in a singleton. – Stephan Rauh Apr 29 '14 at 22:48
  • If you don't want a Singleton to hold your state then you can specify 'prototype=true' when creating a spring bean – SRy Apr 29 '14 at 23:13
2
public class ViewScopeCallbackRegistrer implements ViewMapListener {

  @SuppressWarnings("unchecked")
  @Override
  public void processEvent(SystemEvent event) throws AbortProcessingException {
    if (event instanceof PostConstructViewMapEvent) {
      PostConstructViewMapEvent viewMapEvent = (PostConstructViewMapEvent) event;

      UIViewRoot viewRoot = (UIViewRoot) viewMapEvent.getComponent();

      viewRoot.getViewMap().put(
          ViewScope.VIEW_SCOPE_CALLBACKS,
          new HashMap<String, Runnable>()
      );

    } else if (event instanceof PreDestroyViewMapEvent) {
      PreDestroyViewMapEvent viewMapEvent = (PreDestroyViewMapEvent) event;

      UIViewRoot viewRoot = (UIViewRoot) viewMapEvent.getComponent();

      Map<String, Runnable> callbacks = (Map<String, Runnable>) viewRoot
          .getViewMap().get(ViewScope.VIEW_SCOPE_CALLBACKS);

      if (callbacks != null) {
        for (Runnable c : callbacks.values()) {
          c.run();
        }
        callbacks.clear();
      }
    }
  }

  @Override
  public boolean isListenerForSource(Object source) {
    return source instanceof UIViewRoot;
  }
}
k-var
  • 15
  • 8
Alfaville
  • 543
  • 7
  • 16
1
public class ViewScope implements Scope {

public static final String VIEW_SCOPE_CALLBACKS = "viewScope.callbacks";

@Override
public synchronized Object get(String name, ObjectFactory<?> objectFactory) {
Object instance = this.getViewMap().get(name);
if(instance == null){
instance = objectFactory.getObject();
this.getViewMap().put(name, instance);
}
return instance;
}

@SuppressWarnings("unchecked")
@Override
public Object remove(String name) {
Object instance = this.getViewMap().remove(name);
if(instance == null){
Map<String, Runnable> callbacks = (Map<String, Runnable>) this.getViewMap().get(VIEW_SCOPE_CALLBACKS);
if(callbacks != null)
callbacks.remove(name);
}
return instance;
}

/**
* Responsável por registrar uma chamada de destruição ao bean
* que será armazenadano [b]viewMap[/b] da [b]ViewRoot[/b](nossa página que será mostrada)
* @see #getViewMap()
* @param name - nome do bean
* @param runnable
*/  
@SuppressWarnings("unchecked")
@Override
public void registerDestructionCallback(String name, Runnable runnable) {
Map<String, Runnable> callbacks = (Map<String, Runnable>) this.getViewMap().get(VIEW_SCOPE_CALLBACKS);
if(callbacks != null)
callbacks.put(name, runnable);
}

@Override
public Object resolveContextualObject(String key) {
FacesContext facesContext = FacesContext.getCurrentInstance();
FacesRequestAttributes facesResquestAttributes = new FacesRequestAttributes(facesContext);
return facesResquestAttributes.resolveReference(key);
}

@Override
public String getConversationId() {
FacesContext facesContext = FacesContext.getCurrentInstance();
FacesRequestAttributes facesResquestAttributes = new FacesRequestAttributes(facesContext);  
return facesResquestAttributes.getSessionId() + "-" + facesContext.getViewRoot().getViewId();
}

private Map<String, Object> getViewMap(){
return FacesContext.getCurrentInstance().getViewRoot().getViewMap();
}

}
Alfaville
  • 543
  • 7
  • 16
-2

I have tried a work around for the Jsf view bean memory leak issue for both Jsf 2.1 & Jsf 2.2. Try the code in following link Memory leak with ViewScoped bean?. It will clear the view bean in session while navigating to next page.

Community
  • 1
  • 1
  • 1
    What are you talking about is not JSF specific. And totally unrelated to this question, BTW. – Aritz Jul 14 '14 at 06:25