Spring"

From Documentation
 
(51 intermediate revisions by 5 users not shown)
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: allow 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/product/zkspring.dsp 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 ==
+
= Configuration =  
Before using Spring, you have to download it, and put the jar file into the directory of your web application.
+
The minimal Maven dependency you need is :
  
# Download Spring Core framework 3.0.2 release binaries [http://www.springframework.org/download download]
+
<source lang='xml'>
# Since 3.0.x release Spring is distributed as a collection fo Spring module jar files instead of a single spring.jar. You need following Spring module jar files on your class-path for using zkspring-core.jar of ZK Spring 3.0RC release.
+
    <dependency>
 +
      <groupId>org.springframework</groupId>
 +
      <artifactId>spring-web</artifactId>
 +
      <version>${spring.version}</version>
 +
    </dependency>
 +
    <dependency>
 +
        <groupId>org.springframework</groupId>
 +
        <artifactId>spring-context</artifactId>
 +
        <version>${spring.version}</version>
 +
    </dependency>
 +
</source>
  
**org.springframework.aop-3.0.2.RELEASE.jar
 
**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><tt>
 
  
Here $myApp represents the name of your web application.</tt>
+
<div style="-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;color:#c06330;padding:15px 40px;background:#fed no-repeat 13px 13px;margin-bottom:10px">
 +
[[File:Icon_info.png ]] '''Note:''' If you don't use Maven, please refer to Spring Framework Reference Documentation to know which JAR file you need.
 +
</div>
  
Note: For earlier ZK Spring 1.1.0 release see [[#ZK Spring 1.1.0 Release configuration details | ZK Spring 1.1.0 Release configuration details]] for copying spring.jar
 
  
== Configure web.xml ==
+
To integrate ZK application with Spring, the minimal configuration you have to setup is the following:
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" >
+
'''Spring related configuration in web.xml'''
<context-param>
+
<source lang='xml'>
  <param-name>contextConfigLocation</param-name>
+
<!-- Loads the Spring application context configuration -->
  <param-value>/WEB-INF/applicationContext.xml</param-value>
+
<listener>
</context-param>
+
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+
</listener>
<listener>
+
<!-- For using web scoped bean -->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+
<listener>
</listener>
+
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
 +
</listener>
 
</source>
 
</source>
  
=Creating Spring Bean Class=
 
Then you have to define a DataSource interface and its implementation:
 
  
<tt>''DataSource.java''</tt>
+
You can enable Spring's classpath scanning to register beans.
 +
 
 +
'''WEB-INF/applicationContext.xml'''
 +
<source lang='xml' highlight='12'>
 +
 
 +
<?xml version="1.0" encoding="UTF-8"?>
 +
<beans xmlns="http://www.springframework.org/schema/beans"
 +
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 +
      xmlns:context="http://www.springframework.org/schema/context"
 +
      xsi:schemaLocation="
 +
          http://www.springframework.org/schema/beans
 +
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 +
          http://www.springframework.org/schema/context
 +
          http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 +
 
 +
<!-- Scans for application @Components to deploy -->
 +
<context:component-scan base-package="org.zkoss.reference.developer" />
  
<source lang="java" >
+
</beans>
package test;
 
 
public interface DataSource
 
{
 
    java.util.List getElementsList();
 
}
 
 
</source>
 
</source>
 +
* Line 12: Apply <code>@Component</code> on those classes that you plan to register them as Spring beans and specify the base package of those classes.
 +
 +
= Access a Spring Bean in a ZUL =
 +
 +
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. Simply put the below directive on top of a ZUML page:
 +
 +
'''<code>&lt;?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver" ?&gt;</code> '''
  
<tt>''DataSourceImpl.java''</tt>
+
Then, on the rest of your page, you can access a Spring-Managed bean directly using its '''bean id'''.
  
<source lang="java" >
+
Assume that we have 2 beans:
package test;
+
 
+
<source lang="java">
import java.util.*;
+
 
+
@Component
public class DataSourceImpl implements DataSource
+
@Scope("session")
{
+
public class UserPreference {
    public List getElementsList()
+
...
    {
+
}
        List list = new ArrayList();
 
        list.add("Tom");
 
        list.add("Henri");
 
        list.add("Jim");
 
       
 
        return list;
 
    }
 
}
 
 
</source>
 
</source>
 +
* User preference should be distinct for each user but shared among multiple requests. It is suitable to be a session-scoped bean.
 +
  
= Accessing Spring Bean in the ZUML page =
+
<source lang="java">
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>.  
+
@Component
 +
public class SystemConfiguration {
 +
...
 +
}
 +
</source>
 +
* As system configuration should be shared within the whole application, this should be a singleton bean.
  
== Using variable-Resolver ==
 
Simply declare the <tt>variable-resolver</tt> with <tt>org.zkoss.zkplus.spring.DelegatingVariableResolver</tt> 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" >
+
'''Access Spring beans with EL'''
 +
<source lang="xml" highlight="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 <code>userPreference</code> automatically for you.
  
== Using SpringUtil ==
+
= Wire a Spring Bean =
<tt>org.zkoss.zkplus.spring.SpringUtil</tt> is a utility class which allows you to get Spring-managed beans in Java code with ease.  
+
It is a very common requirement that we need a Spring bean in a composer, e.g. calling an authentication bean to do login. You can inject Spring beans into ZK Composer/ViewModel in the following ways.
  
<source lang="xml" >
+
== Spring's @Autowire ==
<window>
+
ZK stores a Composer/ViewModel as a component's attribute, and you can dynamically add/remove components at any time. If you want a fresh new state of a component when each time you add/create it, you should declare a Composer/ViewModel in <code>prototype</code> scoped. Then you can use <code>@Autowire</code> to wire Spring beans into a Composer/ViewModel.
  <zscript><![CDATA[
+
 
  import org.zkoss.zkplus.spring.SpringUtil;
+
<!--
  import test.*;
+
this section might have problems. injecting a singleton service bean (longer-lived) into a composer (shorter-lived) should not need a proxy.
 
+
We need to test it under clustering env.
  DataSource dataSource = SpringUtil.getBean("DataSource");
+
 
  List list = dataSource.getElementsList();
+
== Using Scoped-Proxy ==
]]></zscript>
+
We recommend you to apply scoped-proxy on every bean that is used in a composer( or a ViewModel). There are at least two reasons. First, [https://docs.spring.io/spring-framework/docs/5.3.9/reference/html/core.html#beans-factory-scopes-other-injection Spring Framework Reference] mentions that when you inject a shorter-lived scoped bean into a longer-lived scoped bean, you should use scoped-proxy bean. The proxy can fetch the real shorter-lived scoped bean for a longer-lived scoped bean and delegate all method calls onto the real bean. Because the scope of composer ( or a ViewModel) doesn't match any of Spring's bean scopes, using scoped-proxy ensures you call a method on the right bean. Second, a reference to a bean might be invalid after session replication in clustering environment. Hence, even you use a singleton bean, it's better to use the scoped-proxy.
 
+
 
<grid>
+
To apply scoped-proxy to a bean, just apply <code>@Scope</code> with <code>proxyMode</code> specified on a bean.
  <rows>
+
 
    <row forEach="${list}">
+
<source lang='java' highlight='2'>
      <label value="${each}"/>
+
@Service("orderService")
    </row>
+
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
  </rows>
+
public class OrderServiceImpl implements OrderService {
</grid>
+
...
 +
}
 +
</source>
 +
* Line 2: If you use <code>ScopedProxyMode.TARGET_CLASS</code>, you need CGLIB library in the classpath of your application.
 +
 
 +
-->
 +
 
 +
== Wire a Spring bean in a Composer ==
 +
Alternatively, ZK provides another way to wire a Spring bean to a composer which is not a Spring-managed bean. When we apply a <code>SelectorComposer</code> to a ZUL with <code>org.zkoss.zkplus.spring.DelegatingVariableResolver</code> mentioned in the previous section,  we can apply annotation, <code>@WireVariable</code> on a variable we want to wire a Spring bean with. ZK will then wire the corresponding Spring bean on the variable using a variable name that's the same as the bean's name. Or, you can specify the bean's name with <code>@WireVariable("beanName")</code>.
 +
 
 +
 
 +
'''A composer that wires Spring beans'''
 +
<source lang="java" highlight="1, 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>
 +
* Line 1: <code>@WireVariable("beanName")</code> only works inside a <code>SelectorComposer</code>.
 +
 
 +
 
 +
'''A ZUL with Spring variable resolver'''
 +
<source lang="xml" highlight="1,3">
 +
 
 +
<?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>
 
</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.
 
  
=Version History=
+
== Wire a Spring bean in a ViewModel ==
{{LastUpdated}}
+
 
{| border='1px' | width="100%"
+
Wiring a Spring bean in a ViewModel is very similar to the case in a composer, simply apply <code>@WireVariable</code> with variable resolver. In the example below we put variable resolver in a zul with a directive.
! Version !! Date !! Content
+
 
|-
+
'''A ViewModel that wires a Spring bean'''
| &nbsp;
+
<source lang="java" highlight="3">
| &nbsp;
+
 
| &nbsp;
+
public class OrderVM {
|}
+
 
 +
@WireVariable
 +
private OrderService orderService;
 +
 
 +
...
 +
}
 +
</source>
 +
 
 +
'''The zul uses OrderVM with a Spring variable resolver'''
 +
<source lang="xml" highlight="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')">
 +
 
 +
...
 +
</window>
 +
</zk>
 +
</source>
 +
 
 +
== Adding Variable Resolver to a Composer/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
 +
 
 +
'''<code>@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)</code>'''
 +
 
 +
on '''the class that inherits <code>SelectorComposer</code> or a ViewModel'''. Then, apply <code>@WireVariable</code> on variables like we did in the previous section.
 +
 
 +
Example code are as follows:
 +
 
 +
<source lang="java" highlight='1'>
 +
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
 +
public class SpringComposer extends SelectorComposer<Window> {
 +
 
 +
@WireVariable
 +
private OrderService orderService;
 +
...
 +
}
 +
</source>
 +
 
 +
= Retrieve a Spring Bean Programmatically=
 +
 
 +
<code>org.zkoss.zkplus.spring.SpringUtil</code> is a utility class that allows you to get Spring-managed beans by their name in Java.
 +
 
 +
<source lang="java" highlight="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>
 +
 
 +
= 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 =
 +
 
 +
You can get all source code mentioned in this chapter at [https://github.com/zkoss/zkbooks/tree/master/developersreference/integration.spring GitHub/zkoss/zkbooks].
 +
 
  
 
{{ZKDevelopersReferencePageFooter}}
 
{{ZKDevelopersReferencePageFooter}}

Latest revision as of 03:20, 18 January 2023


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.

Configuration

The minimal Maven dependency you need is :

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>


Icon info.png Note: If you don't use Maven, please refer to Spring Framework Reference Documentation to know which JAR file you need.


To integrate ZK application with Spring, the minimal configuration you have to setup is the following:

Spring related configuration in web.xml

	<!-- Loads the Spring application context configuration -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!-- For using web scoped bean -->
	<listener>
		<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>


You can enable Spring's classpath scanning to register beans.

WEB-INF/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- Scans for application @Components to deploy -->
	<context:component-scan base-package="org.zkoss.reference.developer" />

</beans>
  • Line 12: Apply @Component on those classes that you plan to register them as Spring beans and specify the base package of those classes.

Access a Spring Bean in a ZUL

ZUL provides a feature called variable resolver that allows users to access Spring bean using EL expressions. Simply put the below directive on top of a ZUML page:

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

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

Assume that we have 2 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

It is a very common requirement that we need a Spring bean in a composer, e.g. calling an authentication bean to do login. You can inject Spring beans into ZK Composer/ViewModel in the following ways.

Spring's @Autowire

ZK stores a Composer/ViewModel as a component's attribute, and you can dynamically add/remove components at any time. If you want a fresh new state of a component when each time you add/create it, you should declare a Composer/ViewModel in prototype scoped. Then you can use @Autowire to wire Spring beans into a Composer/ViewModel.


Wire a Spring bean in a Composer

Alternatively, ZK provides another way to wire a Spring bean to a composer which is not a Spring-managed bean. When we apply a SelectorComposer 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 on the variable using a variable name that's the same as the bean's name. Or, you can specify the bean's name with @WireVariable("beanName").


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()));
	}

}
  • Line 1: @WireVariable("beanName") only works inside a SelectorComposer.


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
	private OrderService orderService;

	...
}

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/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 that allows you to get Spring-managed beans by their name 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

You can get all source code mentioned in this chapter at GitHub/zkoss/zkbooks.



Last Update : 2023/01/18

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