Spring"

From Documentation
(Undo revision 30663 by Hawk (talk))
(replace with new edition)
Line 1: Line 1:
 
{{ZKDevelopersReferencePageHeader}}
 
{{ZKDevelopersReferencePageHeader}}
  
=Overview=
 
  
[http://www.springframework.org/ Spring] is a platform for building Java application, and it includes many easy-to-use solutions for building web-based application.
 
  
Here we discuss how to use Spring with ZK, especially the use of <javadoc>org.zkoss.zkplus.DelegatingVariableResolver</javadoc>. It provides the basic support of Spring which allows a ZUML document to access variables defined in Spring. For more comprehensive support, such as Spring scopes, annotations and security, please refer to another product: [http://www.zkoss.org/documentation/zkspring ZK Spring].
+
= Overview =
  
=Installing Spring=
+
Spring Framework is a popular application development framework for enterprise Java. One key element is its infrastructural support: a light-weighted container that manages POJOs as Spring beans and maintain beans' dependency injection relationship. We will talk about several integration ways including wiring and accessing beans in various conditions. We assume that readers have knowledge in Spring's basic configuration and concept such as bean scope, we will therefore not cover these topics here. Please refer to [http://www.springsource.org/spring-framework#documentation Spring documentation].
First you have to install Spring to your Web application. If you are familiar with Spring, you could skip this section.
 
In this section we use Spring core 3.0.2 and Spring Security 3.0.2.
 
  
== Copy Spring binaries into your Web library ==
+
= Register a Composer (or ViewModel) as a Spring bean =
Before using Spring, you have to download it, and put the jar file into the directory of your web application.
 
  
# Download Spring Core framework 3.0.2 release binaries [http://www.springframework.org/download download]
+
Many developers tend to register ZK's composer (or ViewModel) as a Spring-managed bean, but we do not recommend this approach because none of Spring's scopes matches correctly with the life cycle of the composers, please see [[ZK Developer's Reference/MVC/Controller/Wire Variables]] for more details. The scope of a composer is "desktop" scope that is shorter than "session" and longer than "prototype". Only ZK knows when to create composers (or ViewModel), so it's better to let composers be managed by ZK.
  
**org.springframework.aop-3.0.2.RELEASE.jar
+
If you insist on making Spring to manage the composers (or ViewModel), "prototype" scope could be a feasible scope. However, you need to use with care; each time you try to resolve a composer bean, you will get a new instance of a composer. If the composer stores some states, it will cause inconsistency of states among multiple composers.
**org.springframework.asm-3.0.2.RELEASE.jar
 
**org.springframework.beans-3.0.2.RELEASE.jar
 
**org.springframework.context-3.0.2.RELEASE.jar
 
**org.springframework.context.support-3.0.2.RELEASE.jar
 
**org.springframework.core-3.0.2.RELEASE.jar
 
**org.springframework.expression-3.0.2.RELEASE.jar
 
**org.springframework.transaction-3.0.2.RELEASE.jar
 
**org.springframework.web-3.0.2.RELEASE.jar
 
**org.springframework.web.servlet-3.0.2.RELEASE.jar
 
 
Put these jar files into your <tt>$myApp/WEB-INF/lib/</tt>. Here <tt>$myApp</tt> represents the name of your web application.
 
  
== Configure web.xml ==
+
= Access a Spring Bean in a ZUL =
In your web.xml, you have to define <tt>org.springframework.web.context.ContextLoaderListener</tt>, and to specify the location of the configuration file to load bean definitions.
 
  
<source lang="xml" high="3">
+
ZUL provides a feature called [[ZK Developer's Reference/UI Composing/ZUML/EL Expressions#Variable Resolver| variable resolver]] that allows users to access Spring bean using EL expressions. This is how you do it; simply put the below directive on top of a ZUML page:
<context-param>
 
  <param-name>contextConfigLocation</param-name>
 
  <param-value>/WEB-INF/applicationContext.xml</param-value>
 
</context-param>
 
 
<listener>
 
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 
</listener>
 
</source>
 
  
== Create Spring Configuration File ==
+
'''<tt>&lt;?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver" ?&gt;</tt> '''
Define bean definitions in <tt>applicationContext.xml</tt> file, and put it into your <tt>WEB-INF</tt> directory.
 
  
<source lang="xml" high="5">
+
Then, in the rest of your page, you can access a Spring-Managed bean directly using its '''bean id'''.
<?xml version="1.0" encoding="UTF-8"?>
 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
 
 
<beans>
 
  <bean id="DataSource" class="test.DataSourceImpl"/>
 
</beans>
 
</source>
 
  
=Creating Spring Bean Class=
+
Assume that we have two beans:
Then you have to define a DataSource interface and its implementation:
 
  
<tt>''DataSource.java''</tt>
+
<source lang="java">
  
<source lang="java" >
+
@Component
package test;
+
@Scope("session")
+
public class UserPreference {
public interface DataSource
+
...
{
+
}
    java.util.List getElementsList();
 
}  
 
 
</source>
 
</source>
 +
* User preference should be distinct for each user but shared among multiple requests. It is suitable to be a session scoped bean.
  
<tt>''DataSourceImpl.java''</tt>
 
  
<source lang="java" >
+
<source lang="java">
package test;
+
@Component
+
public class SystemConfiguration {
import java.util.*;
+
...
+
}
public class DataSourceImpl implements DataSource
 
{
 
    public List getElementsList()
 
    {
 
        List list = new ArrayList();
 
        list.add("Tom");
 
        list.add("Henri");
 
        list.add("Jim");
 
       
 
        return list;
 
    }
 
}
 
 
</source>
 
</source>
 +
* As system configuration should be shared within the whole application, this should be a singleton bean.
  
= Accessing Spring Bean in the ZUML page =
 
There are two ways to access Spring-Managed beans in your ZUML page. One is using <tt>variable-resolver</tt>, and the other is using <tt>SpringUtil</tt>. Which to use depends on your usage, in the ZUML page, we suggest you to use <tt>variable-resolver</tt>.
 
 
== Using variable-Resolver ==
 
Simply declare the <tt>variable-resolver</tt> with <javadoc>org.zkoss.zkplus.spring.DelegatingVariableResolver</javadoc> on top of your ZUML page, then, in the rest of your page, you can access any Spring-Managed beans directly using its bean-id.
 
  
<source lang="xml" high="5">
+
'''Access Spring beans with EL'''
 +
<source lang="xml" high="1,7,11">
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window>
+
<window title="Access Bean with different scopes" border="normal" width="700px"
  <grid>
+
apply="org.zkoss.reference.developer.composer.ResolverComposer">
    <rows>
+
<vlayout>
      <row forEach="${DataSource.elementsList}">
+
<hlayout>
        <label value="${each}"/>
+
User Preference :
      </row>
+
<label id="sessionValue">${userPreference.value}</label>
    </rows>
+
</hlayout>
  </grid>
+
<hlayout>
 +
System Configuration :
 +
<label id="singletonValue">${systemConfiguration.value}</label>
 +
</hlayout>
 +
</vlayout>
 
</window>
 
</window>
 
</source>  
 
</source>  
  
<tt>variable-resolver </tt>will look-up the bean named <tt>DataSource</tt> automatically for you, and returned a list to the <tt>forEach</tt> loop.
+
* The delegating variable-resolver will look-up the bean named <tt>userPreference</tt> automatically for you.
 +
 
 +
= Wire a Spring bean =
 +
== Wire a Spring bean in a Composer ==
 +
 
 +
It is a very common requirement that we need to use a Spring bean in a composer, for example calling a service layer object to perform business logic. If a composer is a Spring-managed bean, we can just use Spring's injection mechanism like auto-wire to wire dependencies. However, we don't recommend this approach (explain in the previous section).
 +
 
 +
Therefore, ZK provides another way to wire a Spring bean to a composer which is not a Spring-managed bean. When we apply a composer to a ZUL with  <tt>org.zkoss.zkplus.spring.DelegatingVariableResolver</tt> mentioned in the previous section,  we can apply annotation, <tt>@WireVariable</tt> on a variable we want to wire a Spring bean with. ZK will then wire the corresponding Spring bean with the variable using variable name that's same as the bean's name. Alternatively, you can specify the bean's name with <tt>@WireVariable("beanName")</tt>.  
 +
 
 +
Example code is as follow:
 +
 
 +
 
 +
'''A composer that wires Spring beans'''
 +
<source lang="java" high="3">
 +
 
 +
public class ResolverComposer extends SelectorComposer<Window> {
 +
 
 +
@WireVariable
 +
private OrderService orderService;
 +
 +
@Wire("#number")
 +
private Label label;
 +
 +
@Override
 +
public void doAfterCompose(Window comp) throws Exception {
 +
super.doAfterCompose(comp);
 +
label.setValue(Integer.toString(orderService.list().size()));
 +
}
 +
 
 +
}
 +
</source>
  
===Use with Composer===
+
'''A ZUL with Spring variable resolver'''
<javadoc>org.zkoss.zk.ui.util.GenericAutowireComposer</javadoc> will wire the variables defined in the variable resolvers. Thus, we could declare <javadoc>org.zkoss.zkplus.spring.DelegatingVariableResolver</javadoc> in the ZUML document, and then declare the Spring-managed bean as a control directly in a composer. For example,
+
<source lang="xml" high="1,3">
  
<source lang="xml" high="1">
 
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window apply="foo.MyComposer">
+
<window title="Access Bean with different scopes" border="normal" width="700px"
 +
apply="org.zkoss.reference.developer.spring.composer.ResolverComposer">
 
...
 
...
    <textbox id="password"/>
+
</window>
 +
</source>
 +
 
 +
== Wire a Spring bean in a ViewModel ==
 +
 
 +
Wiring a Spring bean in a ViewModel is very similar to the case in a composer, simply apply <tt>@WireVariable</tt> with variable resolver. In the example below we put variable resolver in a zul with a directive.
 +
 
 +
'''A ViewModel that wires a Spring bean'''
 +
<source lang="java" high="3">
 +
 
 +
public class OrderVM {
 +
 
 +
@WireVariable
 +
OrderService orderService;
 +
 
 +
public List<Order> getOrders() {
 +
if (orders == null) {
 +
//init the list
 +
orders = orderService.list();
 +
}
 +
return orders;
 +
}
 +
...
 +
}
 +
</source>
 +
 
 +
'''The zul uses OrderVM with a Spring variable resolver'''
 +
<source lang="xml" high="1,4">
 +
 
 +
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 +
<zk>
 +
<window title="Order Management" border="normal" width="600px" apply="org.zkoss.bind.BindComposer"
 +
viewModel="@id('vm') @init('org.zkoss.reference.developer.spring.order.viewmodel.OrderVM')"
 +
validationMessages="@id('vmsgs')">
 +
 
 
...
 
...
    <button id="submit" label="Change"/>
 
 
</window>
 
</window>
 +
</zk>
 
</source>
 
</source>
  
Then, if a data member's name matches a Spring-managed bean, it will be wired automatically too. For example,
+
== Adding Variable Resolver to a Composer (or ViewModel) ==
 +
 
 +
Adding a variable resolver to a ZUL will make it available to all composers on the ZUL. If you want to add a variable resolver to a specific composer (or ViewModel) only, you should apply the annotation
 +
 
 +
'''<tt>@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)</tt>'''
 +
 
 +
on '''the class that inherits <tt>SelectorComposer</tt> or a ViewModel'''. Then, apply <tt>@WireVariable</tt> on variables like we did in the previous section.  
 +
 
 +
Example code are as follows:
  
<source lang="java" high="2">
+
<source lang="java">
public class PasswordSetter extends GenericFowardComposer {
+
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
    private User user; //wired automatically if user is a spring-managed bean
+
public class SpringComposer extends SelectorComposer<Window> {
    private Textbox password; //wired automatically if there is a textbox named password
 
  
    public void onClick$submit() {
+
@WireVariable
        user.setPassword(password.getValue());
+
private OrderService orderService;
    }
+
...
 
}
 
}
 
</source>
 
</source>
  
For more information, please refer to [[ZK Developer's Reference/MVC/Controller/Wire Variables|MVC: Controller]].
+
= Retrieve a Spring Bean Programmatically=
  
== Using SpringUtil ==
+
<tt>org.zkoss.zkplus.spring.SpringUtil</tt> is a utility class which allows you to get Spring-managed beans in Java.
<tt>org.zkoss.zkplus.spring.SpringUtil</tt> is a utility class which allows you to get Spring-managed beans in Java code with ease.  
+
 
 +
<source lang="java" high="9">
 +
public class SpringComposer extends SelectorComposer<Window> {
 +
 
 +
@Wire("#number")
 +
private Label label;
 +
 +
@Override
 +
public void doAfterCompose(Window comp) throws Exception {
 +
super.doAfterCompose(comp);
 +
OrderService orderService = (OrderService)SpringUtil.getBean("orderService");
 +
label.setValue(Integer.toString(orderService.list().size()));
 +
}
 +
}
 +
</source>
 +
 
 +
<!--
 +
= Tips =
 +
 
 +
== Use Spring's Scoped Proxy ==
 +
 
 +
If you inject a request and session ('''shorter-lived scoped''') bean into a singleton ('''longer-lived scoped''') bean, you need to inject a proxy object that can also retrieve the real bean and delegate method calls onto the real bean. Because Spring only injects dependent beans once when it instantiates the bean. If we don't use scoped proxy, the longer-lived scoped bean will keep referencing first-injected beans even those injected beans are out of scope.
 +
 
 +
Let's see an example. Assume that we have a session scope bean (userData) which is injected to a singleton bean (userManager). If we don't use a scoped proxy, userManager always references to the same userData instead of different userData objects for different sessions.
 +
 
 +
To create such a proxy, use <tt>&lt;aop:scoped-proxy/&gt;</tt> element in a bean or you can specify <tt>scopedProxyMode.TARGET_CLASS</tt> or <tt>ScopedProxyMode.INTERFACES</tt> in Java with a <tt>@Scope</tt> annotation.
 +
 
 +
<source lang="xml" high="2">
 +
 
 +
<bean id="userData" class="example.UserData" scope="session">
 +
    <aop:scoped-proxy/>
 +
</bean>
 +
 
 +
<bean id="userManager" class="example.UserManager">
 +
    <property name="userData" ref="userData"/>
 +
</bean>
  
<source lang="xml" high="6">
 
<window>
 
<zscript><![CDATA[
 
  import org.zkoss.zkplus.spring.SpringUtil;
 
  import test.*;
 
 
 
  DataSource dataSource = SpringUtil.getBean("DataSource");
 
  List list = dataSource.getElementsList();
 
]]></zscript>
 
 
 
<grid>
 
  <rows>
 
    <row forEach="${list}">
 
      <label value="${each}"/>
 
    </row>
 
  </rows>
 
</grid>
 
</window>
 
 
</source>
 
</source>
+
 
Where the <tt>forEach</tt> loop is looping over the collection to print the <tt>${each}</tt> attribute on each object in the collection.
+
The scoped proxy bean is injected to userManager and delegate method calls to real userData for different sessions.
 +
 
 +
-->
 +
 
 +
= Integrate Spring Webflow and Security =
 +
 
 +
ZK also provides integration to other Spring projects such as Spring Security and Spring Webflow with ZK Spring. Please refer to [[ZK Spring Essentials]] for details.
 +
 
 +
= Example Source Code =
 +
 
 +
All source code of examples used in this chapter can be found in [https://code.google.com/p/zkbooks/source/browse/#svn%2Ftrunk%2Fdevelopersreference%2Fintegration.spring here].
 +
 
 +
 
 +
 
  
 
=Version History=
 
=Version History=
Line 169: Line 222:
 
! Version !! Date !! Content
 
! Version !! Date !! Content
 
|-
 
|-
| &nbsp;
+
| 6.5.0
| &nbsp;
+
| November 2012
| &nbsp;
+
| Rewrite for improvement.
 
|}
 
|}
  
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Revision as of 02:52, 28 December 2012


Overview

Spring Framework is a popular application development framework for enterprise Java. One key element is its infrastructural support: a light-weighted container that manages POJOs as Spring beans and maintain beans' dependency injection relationship. We will talk about several integration ways including wiring and accessing beans in various conditions. We assume that readers have knowledge in Spring's basic configuration and concept such as bean scope, we will therefore not cover these topics here. Please refer to Spring documentation.

Register a Composer (or ViewModel) as a Spring bean

Many developers tend to register ZK's composer (or ViewModel) as a Spring-managed bean, but we do not recommend this approach because none of Spring's scopes matches correctly with the life cycle of the composers, please see ZK Developer's Reference/MVC/Controller/Wire Variables for more details. The scope of a composer is "desktop" scope that is shorter than "session" and longer than "prototype". Only ZK knows when to create composers (or ViewModel), so it's better to let composers be managed by ZK.

If you insist on making Spring to manage the composers (or ViewModel), "prototype" scope could be a feasible scope. However, you need to use with care; each time you try to resolve a composer bean, you will get a new instance of a composer. If the composer stores some states, it will cause inconsistency of states among multiple composers.

Access a Spring Bean in a ZUL

ZUL provides a feature called variable resolver that allows users to access Spring bean using EL expressions. This is how you do it; simply put the below directive on top of a ZUML page:

<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver" ?>

Then, in the rest of your page, you can access a Spring-Managed bean directly using its bean id.

Assume that we have two beans:

@Component
@Scope("session")
public class UserPreference {
...
}
  • User preference should be distinct for each user but shared among multiple requests. It is suitable to be a session scoped bean.


@Component
public class SystemConfiguration {
...
}
  • As system configuration should be shared within the whole application, this should be a singleton bean.


Access Spring beans with EL

<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window title="Access Bean with different scopes" border="normal" width="700px"
	apply="org.zkoss.reference.developer.composer.ResolverComposer">
	<vlayout>
		<hlayout>
			User Preference :
			<label id="sessionValue">${userPreference.value}</label>
		</hlayout>
		<hlayout>
			System Configuration :
			<label id="singletonValue">${systemConfiguration.value}</label>
		</hlayout>
	</vlayout>
</window>
  • The delegating variable-resolver will look-up the bean named userPreference automatically for you.

Wire a Spring bean

Wire a Spring bean in a Composer

It is a very common requirement that we need to use a Spring bean in a composer, for example calling a service layer object to perform business logic. If a composer is a Spring-managed bean, we can just use Spring's injection mechanism like auto-wire to wire dependencies. However, we don't recommend this approach (explain in the previous section).

Therefore, ZK provides another way to wire a Spring bean to a composer which is not a Spring-managed bean. When we apply a composer to a ZUL with org.zkoss.zkplus.spring.DelegatingVariableResolver mentioned in the previous section, we can apply annotation, @WireVariable on a variable we want to wire a Spring bean with. ZK will then wire the corresponding Spring bean with the variable using variable name that's same as the bean's name. Alternatively, you can specify the bean's name with @WireVariable("beanName").

Example code is as follow:


A composer that wires Spring beans

public class ResolverComposer extends SelectorComposer<Window> {

	@WireVariable
	private OrderService orderService;
	
	@Wire("#number")
	private Label label;
	
	@Override
	public void doAfterCompose(Window comp) throws Exception {
		super.doAfterCompose(comp);
		label.setValue(Integer.toString(orderService.list().size()));
	}

}

A ZUL with Spring variable resolver

<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window title="Access Bean with different scopes" border="normal" width="700px"
	apply="org.zkoss.reference.developer.spring.composer.ResolverComposer">
...
</window>

Wire a Spring bean in a ViewModel

Wiring a Spring bean in a ViewModel is very similar to the case in a composer, simply apply @WireVariable with variable resolver. In the example below we put variable resolver in a zul with a directive.

A ViewModel that wires a Spring bean

public class OrderVM {

	@WireVariable
	OrderService orderService;

	public List<Order> getOrders() {
		if (orders == null) {
			//init the list
			orders = orderService.list();
		}
		return orders;
	}
	...
}

The zul uses OrderVM with a Spring variable resolver

<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<zk>
<window title="Order Management" border="normal" width="600px" apply="org.zkoss.bind.BindComposer" 
viewModel="@id('vm') @init('org.zkoss.reference.developer.spring.order.viewmodel.OrderVM')" 
validationMessages="@id('vmsgs')">

...
</window>
</zk>

Adding Variable Resolver to a Composer (or ViewModel)

Adding a variable resolver to a ZUL will make it available to all composers on the ZUL. If you want to add a variable resolver to a specific composer (or ViewModel) only, you should apply the annotation

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)

on the class that inherits SelectorComposer or a ViewModel. Then, apply @WireVariable on variables like we did in the previous section.

Example code are as follows:

@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class SpringComposer extends SelectorComposer<Window> {

	@WireVariable
	private OrderService orderService;
...
}

Retrieve a Spring Bean Programmatically

org.zkoss.zkplus.spring.SpringUtil is a utility class which allows you to get Spring-managed beans in Java.

public class SpringComposer extends SelectorComposer<Window> {

	@Wire("#number")
	private Label label;
	
	@Override
	public void doAfterCompose(Window comp) throws Exception {
		super.doAfterCompose(comp);
		OrderService orderService = (OrderService)SpringUtil.getBean("orderService");
		label.setValue(Integer.toString(orderService.list().size()));
	}
}


Integrate Spring Webflow and Security

ZK also provides integration to other Spring projects such as Spring Security and Spring Webflow with ZK Spring. Please refer to ZK Spring Essentials for details.

Example Source Code

All source code of examples used in this chapter can be found in here.



Version History

Last Update : 2012/12/28


Version Date Content
6.5.0 November 2012 Rewrite for improvement.



Last Update : 2012/12/28

Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.