Chapter 9: Spring Integration"

From Documentation
Line 99: Line 99:
 
Starting from 2.0, Spring provides an option to detect beans by scanning the classpath. Developers can use annotation (e.g. <tt>@Component</tt>) to register bean definitions in Spring container and this removes the use of XML. We can use <tt>@Component</tt> which is a generic stereotype annotation or those specialized stereotype annotation: <tt>@Controller</tt>, <tt>@Service</tt>, or <tt>@Repository</tt> for presentation, service, persistence layer, respectively. These annotations work equally for registering beans but using specialized annotation makes your classes suited for processing by tools.
 
Starting from 2.0, Spring provides an option to detect beans by scanning the classpath. Developers can use annotation (e.g. <tt>@Component</tt>) to register bean definitions in Spring container and this removes the use of XML. We can use <tt>@Component</tt> which is a generic stereotype annotation or those specialized stereotype annotation: <tt>@Controller</tt>, <tt>@Service</tt>, or <tt>@Repository</tt> for presentation, service, persistence layer, respectively. These annotations work equally for registering beans but using specialized annotation makes your classes suited for processing by tools.
  
When you register a bean, its bean scope is "singleton" by default if you don't specify. Our service class is stateless so that it is suitable to be a singleton-scoped bean.
+
When you register a bean, its bean scope is "singleton" by default if you don't specify. Our service class is stateless so that it is suitable to be a singleton-scoped bean. For those beans used in composers, you should use scoped-proxy to ensure every time you get the latest bean.
 +
 
  
 
<source lang='java' high='1, 2'>
 
<source lang='java' high='1, 2'>
Line 111: Line 112:
  
  
If you want to specify a bean's scope, use <tt>@Scope</tt>. For those web scopes ("session" and "request"), you should use scoped-proxy to ensure every time you get the latest bean.
+
If you want to specify a bean's scope, use <tt>@Scope</tt>.  
 
 
 
<source lang='java' high='2'>
 
<source lang='java' high='2'>
 
@Component("sidebarPageConfigPagebase")
 
@Component("sidebarPageConfigPagebase")

Revision as of 07:01, 1 February 2013

Overview

Spring Framework is a popular application development framework for enterprise Java. One key element is its infrastructural support: a light-weighted IoC (Inversion of Control) container that manages POJOs as Spring beans and their dependency relationship.

In this chapter, we won't create new example applications but will make previous examples integrated with Spring.

How to Integrate Spring Framework

The most common integration way is to let Spring manage an application's dependencies.

Configuration

Maven

In order to integrate Spring, we must add dependencies for Spring. The cglib is an optional dependency. We add it because our application uses proxy beans that requires it.

Extracted from pom.xml

	<properties>
		<zk.version>6.5.1</zk.version>
		<maven.build.timestamp.format>yyyy-MM-dd</maven.build.timestamp.format>
		<packname>-${project.version}-FL-${maven.build.timestamp}</packname>
		<spring.version>3.1.2.RELEASE</spring.version>
	</properties>
...
		<!-- Spring 3 dependencies -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2.2</version>
		</dependency>

Deployment Descriptor

The deployment descriptor (web.xml) also needs two more listeners.

Extracted from web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> 

	<description><![CDATA[ZK Tutorial]]></description>
	<display-name>ZK Tutorial</display-name>

	<!-- ZK configuration-->
	...
	
	<!-- Spring configuration -->
	<!-- Initialize spring context -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!-- Enable webapp Scopes-->
	 <listener>
    	<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
	</listener>
	
	<welcome-file-list>
		<welcome-file>index.zul</welcome-file>
	</welcome-file-list>
</web-app>
  • Line 16: The ContextLoaderListener reads Spring configuration, and default location is WEB-INF/applicationContext.xml.
  • Line 20: Use RequestContextListener to support web-scoped beans (request, session, global session).


Create Spring configuration file with default name (applicationContext.xml). We enable Spring's classpath scanning to detect and register those class with annotation as beans automatically.

WEB-INF/applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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">
 
 	<context:component-scan base-package="org.zkoss.tutorial" />
 
</beans>
  • Line 10: This configuration enables scanning classpath. Spring will automatically detect those classes with Spring bean annotations and register them in bean definitions. For base-package, you should specify a common parent package or a comma-separated list that includes all candidate classes.

Register Spring Beans

Starting from 2.0, Spring provides an option to detect beans by scanning the classpath. Developers can use annotation (e.g. @Component) to register bean definitions in Spring container and this removes the use of XML. We can use @Component which is a generic stereotype annotation or those specialized stereotype annotation: @Controller, @Service, or @Repository for presentation, service, persistence layer, respectively. These annotations work equally for registering beans but using specialized annotation makes your classes suited for processing by tools.

When you register a bean, its bean scope is "singleton" by default if you don't specify. Our service class is stateless so that it is suitable to be a singleton-scoped bean. For those beans used in composers, you should use scoped-proxy to ensure every time you get the latest bean.


@Service("authService")
@Scope(value="singleton",proxyMode=ScopedProxyMode.TARGET_CLASS)
public class AuthenticationServiceImpl implements AuthenticationService,Serializable{
...
}
  • Line 1: You could specify bean's name in @Service or its bean is derived from class name with first character in lower case (e.g. authenticationServiceImpl in this case).


If you want to specify a bean's scope, use @Scope.

@Component("sidebarPageConfigPagebase")
@Scope(value="request",proxyMode=ScopedProxyMode.TARGET_CLASS)
public class SidebarPageConfigPagebaseImpl implements SidebarPageConfig{
...
}

Retrieve Spring Beans

After registering beans for service classes, we can "wire" them in our controllers with ZK's variable resolver. You might think why we just also register our controllers(or ViewModels) as Spring beans, but we don't recommend to do so. The main reason is that none of Spring bean's scope matches ZK's composer's life cycle, for detail please refer to Developer's Reference.

To wire a Spring bean in a composer, we need

@VariableResolver(DelegatingVariableResolver.class)
public class SidebarChapter4Controller extends SelectorComposer<Component>{

	private static final long serialVersionUID = 1L;
	
	//wire components
	@Wire
	Grid fnList;
	
	//wire service
	@WireVariable("sidebarPageConfigPagebase")
	SidebarPageConfig pageConfig;

	...
}


@VariableResolver(DelegatingVariableResolver.class)
public class ProfileViewModel implements Serializable{
	private static final long serialVersionUID = 1L;

	//wire services
	@WireVariable
	AuthenticationService authService;
	@WireVariable
	UserInfoService userInfoService;
	...
}