A simple JSF application app has a jsf file a.faces and a jsp file b.jsp. The jsp file has the following:
<%
HttpSession sess = rquest.getSession();
FooBean foo = (FooBean)sess.getAttribute("fooBean");
foo.someMethod();
%>
And the jsf file has the following:
<ice:form onsubmit="#{fooBean.doAction}">
Here the tag ice is used because it is using icefaces.
The name fooBean is for a Spring bean defined in applicationContext.xml as below:
<bean id="fooBean" class="FooBean" scope="session">
<constructor-arg><ref bean="x" /> <constructor-arg>
<constructor-arg><ref bean="y" /> <constructor-arg>
</bean>
<bean id="x" class="X"/>
<bean id="y" class="Y" scope="session"/>
And in the faces-config.xml, there is the following:
<faces-config>
<application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
</application>
</faces-config>
The application is accessed by app/a.faces. And then you can access the jsp file app/b.jsp without any problem. But
if you access the application using app/b.jsp as the first step, it will give NullPointerException when calling foo.someMethod() because foo is null.
The tricky thing here is how the "fooBean" is put into the session as an attribute. The answer is in the Spring DelegatingVariableResolver class and DefaultListableBeanFactory class.
When user types app/a.faces, the resolver sees #{fooBean.doAction}. It checks the applicationContext.xml and finds the bean definition. So it creates
the bean x, y, and then fooBean. The bean x is a singleton. It is already created at the application startup. So the resolver only needs to get the cached
instance of x. But the bean y has a scope of session. So the resolver calls DefaultListableBeanFactory and creates a brand new instance of y. And it puts it as an attribute into the HTTP Session object. After both x and y are created, a new fooBean is created and put into the session as an attribute since its scope is also session.
So these session beans are created lazily when there are referenced.
Now if you access app/b.jsp without firstly accessing app/a.faces, the Spring resolver is not invoked. The session object has no attribute named "fooBean". And you will get a null object for this attribute.
The following is the method stacks showing the Spring bean is resolved and set into the HTTP session as an attribute.
owns: InterceptingServletSession (id=15648)
MyAttributeListener.attributeAdded(HttpSessionBindingEvent) line: 13
EventsManager.notifySessionAttributeChange(HttpSession, String, Object, Object) line: 302
MemorySessionContext(SessionContext).notifySessionAttributeChange(HttpSession, String, Object, Object) line: 1479
MemorySessionData(SessionData).setAttribute(String, Object, boolean) line: 1121
MemorySessionData(SessionData).setAttribute(String, Object) line: 959
InterceptingServletSession(ProxyHttpSession).setAttribute(String, Object) line: 107
ServletSessionAttributeMap.setAttribute(String, Object) line: 20
ServletSessionAttributeMap(AbstractAttributeMap).put(Object, Object) line: 157
FacesRequestAttributes.setAttribute(String, Object, int) line: 108
SessionScope(AbstractRequestAttributesScope).get(String, ObjectFactory) line: 44
SessionScope.get(String, ObjectFactory) line: 90
DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class, Object[], boolean) line: 298
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 185
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 164
XmlWebApplicationContext(AbstractApplicationContext).getBean(String) line: 881
DelegatingVariableResolver.resolveSpringBean(FacesContext, String) line: 136
DelegatingVariableResolver.resolveVariable(FacesContext, String) line: 109
NamedValue.evaluate(ExpressionInfo) line: 145
ComplexValue.evaluate(ExpressionInfo) line: 166
ExpressionEvaluatorImpl.evaluate(ExpressionInfo) line: 263
ValueBindingImpl.getValue(FacesContext, String) line: 160
ValueBindingImpl.getValue(FacesContext) line: 143
HtmlForm(HtmlForm).getOnsubmit() line: 501