Spring"

From Documentation
m ((via JWB))
(4 intermediate revisions by the same user not shown)
Line 53: Line 53:
  
 
'''WEB-INF/applicationContext.xml'''
 
'''WEB-INF/applicationContext.xml'''
<source lang='xml' high='12'>
+
<source lang='xml' highlight='12'>
  
 
<?xml version="1.0" encoding="UTF-8"?>
 
<?xml version="1.0" encoding="UTF-8"?>
Line 70: Line 70:
 
</beans>
 
</beans>
 
</source>
 
</source>
* Line 12: Apply <tt>@Component</tt> on those classes that you plan to register them as Spring beans and specify base package of those classes.
+
* Line 12: Apply <code>@Component</code> on those classes that you plan to register them as Spring beans and specify base package of those classes.
  
 
= Access a Spring Bean in a ZUL =
 
= Access a Spring Bean in a ZUL =
Line 76: Line 76:
 
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:
 
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:
  
'''<tt>&lt;?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver" ?&gt;</tt> '''
+
'''<code>&lt;?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver" ?&gt;</code> '''
  
 
Then, in the rest of your page, you can access a Spring-Managed bean directly using its '''bean id'''.
 
Then, in the rest of your page, you can access a Spring-Managed bean directly using its '''bean id'''.
Line 103: Line 103:
  
 
'''Access Spring beans with EL'''
 
'''Access Spring beans with EL'''
<source lang="xml" high="1,7,11">
+
<source lang="xml" highlight="1,7,11">
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 
<window title="Access Bean with different scopes" border="normal" width="700px"
 
<window title="Access Bean with different scopes" border="normal" width="700px"
Line 120: Line 120:
 
</source>  
 
</source>  
  
* The delegating variable-resolver will look-up the bean named <tt>userPreference</tt> automatically for you.
+
* The delegating variable-resolver will look-up the bean named <code>userPreference</code> automatically for you.
  
= Wire a Spring bean =
+
= Wire a Spring Bean =
  
 
== Using Scoped-Proxy ==
 
== Using Scoped-Proxy ==
 
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, in "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  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.
 
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, in "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  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.
  
To apply scoped-proxy to a bean, just apply <tt>@Scope</tt> with <tt>proxyMode</tt> specified on a bean.
+
To apply scoped-proxy to a bean, just apply <code>@Scope</code> with <code>proxyMode</code> specified on a bean.
  
<source lang='java' high='2'>
+
<source lang='java' highlight='2'>
 
@Service("orderService")
 
@Service("orderService")
 
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
 
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
Line 136: Line 136:
 
}
 
}
 
</source>
 
</source>
* Line 2: If you use <tt>ScopedProxyMode.TARGET_CLASS</tt>, you need CGLIB library in the classpath of your application.
+
* 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 ==
 
== Wire a Spring bean in a Composer ==
Line 142: Line 142:
 
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).  
 
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>.  
+
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  <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 with the variable using variable name that's same as the bean's name. Alternatively, you can specify the bean's name with <code>@WireVariable("beanName")</code>.  
  
 
Example code is as follow:
 
Example code is as follow:
Line 148: Line 148:
  
 
'''A composer that wires Spring beans'''
 
'''A composer that wires Spring beans'''
<source lang="java" high="3">
+
<source lang="java" highlight="3">
  
 
public class ResolverComposer extends SelectorComposer<Window> {
 
public class ResolverComposer extends SelectorComposer<Window> {
Line 168: Line 168:
  
 
'''A ZUL with Spring variable resolver'''
 
'''A ZUL with Spring variable resolver'''
<source lang="xml" high="1,3">
+
<source lang="xml" highlight="1,3">
  
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
Line 179: Line 179:
 
== Wire a Spring bean in a ViewModel ==
 
== 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.
+
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.
  
 
'''A ViewModel that wires a Spring bean'''
 
'''A ViewModel that wires a Spring bean'''
<source lang="java" high="3">
+
<source lang="java" highlight="3">
  
 
public class OrderVM {
 
public class OrderVM {
Line 201: Line 201:
  
 
'''The zul uses OrderVM with a Spring variable resolver'''
 
'''The zul uses OrderVM with a Spring variable resolver'''
<source lang="xml" high="1,4">
+
<source lang="xml" highlight="1,4">
  
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
 
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
Line 218: Line 218:
 
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  
 
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>'''
+
'''<code>@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)</code>'''
  
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.  
+
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:
 
Example code are as follows:
  
<source lang="java" high='1'>
+
<source lang="java" highlight='1'>
 
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
 
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
 
public class SpringComposer extends SelectorComposer<Window> {
 
public class SpringComposer extends SelectorComposer<Window> {
Line 236: Line 236:
 
= Retrieve a Spring Bean Programmatically=  
 
= Retrieve a Spring Bean Programmatically=  
  
<tt>org.zkoss.zkplus.spring.SpringUtil</tt> is a utility class which allows you to get Spring-managed beans in Java.  
+
<code>org.zkoss.zkplus.spring.SpringUtil</code> is a utility class which allows you to get Spring-managed beans in Java.  
  
<source lang="java" high="9">
+
<source lang="java" highlight="9">
 
public class SpringComposer extends SelectorComposer<Window> {
 
public class SpringComposer extends SelectorComposer<Window> {
  
Line 262: Line 262:
 
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.  
 
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.
+
To create such a proxy, use <code>&lt;aop:scoped-proxy/&gt;</code> element in a bean or you can specify <code>scopedProxyMode.TARGET_CLASS</code> or <code>ScopedProxyMode.INTERFACES</code> in Java with a <code>@Scope</code> annotation.
  
<source lang="xml" high="2">
+
<source lang="xml" highlight="2">
  
 
<bean id="userData" class="example.UserData" scope="session">
 
<bean id="userData" class="example.UserData" scope="session">
Line 293: Line 293:
 
= Example Source Code =
 
= 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].
+
All source code of examples used in this chapter can be found in [https://github.com/zkoss/zkbooks/tree/master/developersreference/integration.spring here].
 
 
 
 
 
 
  
 
=Version History=
 
=Version History=
 
{{LastUpdated}}
 
{{LastUpdated}}
{| border='1px' | width="100%"
+
{| class='wikitable' | width="100%"
 
! Version !! Date !! Content
 
! Version !! Date !! Content
 
|-
 
|-

Revision as of 14:11, 12 January 2022


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. We write these source code examples based on Spring 3.0.5.RELEASE.


Configuration

The minimal Maven dependency you need is :

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>3.0.5.RELEASE</version>
    </dependency>

If you use scoped-proxy with CGLIB-based class proxy, you will need cglib.

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</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 and Spring , the minimal configuration you have to setup is 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 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. 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

Using Scoped-Proxy

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, in "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 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.

To apply scoped-proxy to a bean, just apply @Scope with proxyMode specified on a bean.

@Service("orderService")
@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)
public class OrderServiceImpl implements OrderService {
...
}
  • Line 2: If you use ScopedProxyMode.TARGET_CLASS, you need CGLIB library in the classpath of your application.

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


Warning: 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 manage the composers (or ViewModel), "prototype" scope is 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.

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 : 2022/01/12


Version Date Content
6.5.0 November 2012 Rewrite for improvement.



Last Update : 2022/01/12

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