You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by sk...@apache.org on 2007/10/31 16:01:27 UTC
svn commit: r590700 -
/myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml
Author: skitching
Date: Wed Oct 31 08:01:26 2007
New Revision: 590700
URL: http://svn.apache.org/viewvc?rev=590700&view=rev
Log:
Add section about "request scoped facade" solution to component bindings.
General tidyups of documentation.
Modified:
myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml
Modified: myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml
URL: http://svn.apache.org/viewvc/myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml?rev=590700&r1=590699&r2=590700&view=diff
==============================================================================
--- myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml (original)
+++ myfaces/orchestra/trunk/core/src/site/xdoc/component-bindings.xml Wed Oct 31 08:01:26 2007
@@ -28,52 +28,78 @@
<body>
<section name="Component Bindings">
<subsection name="Introduction">
- <subsubsection name="Basic discrepancy">
- While the best pratice is to have a single page controller under conversation per page or
- for a full multi page flow, there is a discrepancy between the scope length needed for the conversation
- and the scope length for associated component bindings. This discrepancy is not related to Orchestra;
- it is much more related to the JSF-specification itself.
+ <subsubsection name="Basic discrepancy">
+ <p>
+ JSF components interact with backing beans. However the lifecycles of these two types of
+ objects are very different; beans may last just a request, or longer (eg a session bean).
+ A component logically may last a number of page views, but physically new instances are
+ created for each request, when the view is "restored".
+ </p>
+ <p>
+ JSF is designed so that in normal cases views "pull" data from backing beans, "push" data entered by the
+ user into backing beans, and "invoke" event-handler methods on backing beans. In all cases, the dependency
+ is unidirectional: views know about beans, but beans do not know about views. And the coupling is done
+ only via EL strings, not hard references. This means that the lifecycles of the view components and
+ the backing beans they reference are decoupled, and the lifecycle mismatch is not an issue.
+ </p>
+ <p>
+ However JSF does provide a mechanism for backing beans to access view components in the rare cases where
+ this is necessary: "bindings". Unfortunately the coupling here is done via direct object references, not
+ EL strings. And if a bean then holds on to these references then very bad things can happen if the
+ lifetime of the bean is longer than a request. Just one of the possible problems is the ugly
+ "duplicate component id" error.
+ </p>
+ <p>
+ Note that "bindings" are not generally a good idea, and should be avoided where possible. In the
+ remainder of this page we assume that you have some very good reason for wanting bindings, and
+ show you how they can be used with non-request-scoped beans.
+ </p>
</subsubsection>
</subsection>
<subsection name="Problem">
- The main problem is that component bindings need to be in a request-scoped backing-bean.
- The component tree is regenerated at every request. Longer component bindings
- are a possible cause for internal id clashes etc...
- <p> </p>
- Now with what we have advocated so far, we have a single page controller/model object which is associated
- with the view. The page controller/model now is stateful, which is exactly what we wanted, however we need
- component bindings in this bean and those are referenced by the view.
- The bindings under normal circumstances inherit exactly the scope of the page controller.
- <p> </p>
+ <p>
+ In order to avoid the lifecycle mismatch between components and beans, any bean that stores a component
+ binding must be request-scoped.
+ </p>
+ <p>
+ However what has been recommended in the "Orchestra best practices" is a flash-scoped backing bean for
+ a view and an additional (flash or manual) conversation-scoped controller for multi-page conversations.
+ The page controller/model now is stateful, which is exactly what we wanted, however when component bindings
+ are needed then they should not be stored on these beans because they are not request-scoped.
+ </p>
+ <p>
Therefore the following construct is bound to fail:
<pre>
<component binding="#{viewcontrollerbean.bindingattribute}" />
</pre>
due to the difference in scopes.
-
- At the first glance, this problem is impossible to solve.
- With more thoughts, Spring itself provides the solution. Since every bean referenced by
- the framework and the view layer is a Spring bean, we get the solution out of the "magical
- Spring toolset".
+ </p>
+ <p>
+ There are two possible solutions to this problem.
+ </p>
</subsection>
- <subsection name="Solution">
+
+ <subsection name="Solution 1: Spring aop-proxy">
+ <p>
Spring in 2.0 has introduced a construct called aop-proxy. This is a special tag which can be embedded
into beans which basically does nothing more than to weave a proxy object around an existing object which inherits
the scope of the referencing bean.
The inner part of the proxy or own bean then can have a scope of its own which can be smaller than the scope
of the referencing object.
- <p> </p>
- So how does this help:
- Lets look again at our example
+ </p>
+ <p>
+ So how does this help? Lets look again at our example
<pre>
<component binding="#{viewcontrollerbean.componentbindingmodel.bindingattribute}" />
</pre>
+ </p>
+ <p>
The accessor path has slightly changed, we have introduced a second bean; a model
bean which specifically holds our component bindings.
- <p> </p>
+ </p>
+ <p>
What happens on the spring configuration side is simply the following:
-
<pre>
<bean id="componentbindingmodel" scope="request" class="path.to.our.model.class">
<aop:scoped-proxy />
@@ -85,17 +111,44 @@
</bean>
</pre>
-The associated component binding model class is a class which only holds the components as attributes and provides associated setter
-and getter methods.
- <p> </p>
- What happens at call stage: The viewcontrollerbean is instantiated under conversation scope, the proxy
- object is generated and assigned depending on its lazy binding stage.
- At the request end, the content of the componentbindingmodel bean is dropped and only the empty proxy
- shell is referenced.
- At the next request - which references parts of the componentbindingmodel object - the content is initialized again
- and can be accessed. With this, it is possible to bind the request scoped component bindings to any long running scope
- no matter if they are custom conversation scopes or the session scope!
+ </p>
+ <p>
+ The associated component binding model class is a class which only holds the components as attributes and
+ provides associated setter and getter methods. The viewcontrollerbean class can then invoke its
+ methods to get access to the bound component instances.
+ </p>
+ <p>
+ When the viewcontrollerbean object is instantiated, Spring sees that it must inject the bean named
+ "componentbindingmodel" into it. However that bean is marked as an aop:proxy, so what spring injects
+ is just a "proxy" object, not a real componentbindingmodel bean. Whenever the viewcontrollerbean
+ then invokes a method on that proxy object, the proxy looks up bean "componentbindingmodel" in
+ the specified scope (request), then invokes that method on the object it found. This does have a
+ significant performance impact; each method call on the proxy performs the bean lookup again.
+ However it always acts on the "latest" version of that bean, which would not happen if a real
+ reference had been held. At the end of the request, the request-scoped componentbindingmodel object
+ is discarded as normal, and only the proxy "shell" remains. At the next request, a new componentbindingmodel
+ instance is created and added into the request scope, and methods invoked on the proxy will then
+ automatically work against this new object.
+ </p>
</subsection>
+ <subsection name="Solution 2: A request-scoped facade">
+ <p>
+ Only a few of the methods on the non-request-scoped backing bean will need access to the bound
+ components. Therefore, these methods can be moved into a request-scoped bean. Add the component
+ binding methods to this request-scoped bean too, and inject a reference to the "real" backing
+ bean into it. Alter the view so that EL expressions that need those component bindings to
+ be evaluated point to the request-scoped bean.
+ </p>
+ <p>
+ Another way of thinking about this is that the "real" backing bean for the page is implemented as
+ request-scoped, and any state it needs to retain is pushed into a "helper" object that is of
+ conversation scope. The request-scoped bean has access to the components without problems.
+ </p>
+ <p>
+ While either of these solutions means yet another backing-bean class for the view, there is no
+ "spring magic" required.
+ </p>
+ </subsection>
</section>
</body>
</document>