1

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

Kuba Spatny
  • 26,618
  • 9
  • 40
  • 63

1 Answers1

3

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:


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.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I just found this article of yours http://balusc.blogspot.cz/2010/06/benefits-and-pitfalls-of-viewscoped.html which made me realize that it might be what I am doing wrong.. – Kuba Spatny Dec 03 '13 at 20:44
  • Thank you for your answer. I went with Spring beans because I have some Spring services that I inject. Also I though that Spring has View scope and JSF doesn't so this is a bummer.. – Kuba Spatny Dec 03 '13 at 21:01