Practices Of Using Spring In ZK
Ian YT Tsai, Engineer, Potix Corporation
October 23, 2012
ZK 6
Introduction
Spring Framework is one of the most common frameworks for Java web developers. In this article, I'll introduce how to integrate ZK, Spring and Hibernate together to build a web application. First, I'll list the prerequisites of the demo application. Then I'll focus on the detail configuration of this application stack and guide you through how to use ZK Spring DelegatingVariableResolver in your ZK code with some programming tips of the Spring part design.
If you have already read "Practices Of Using CDI In ZK"[1], this article is the counterpart of it in Spring.
Develop Environment Preparation
You can get source code from github [2]. If your are familiar with Git, you can also clone my repository.
IDE Setup
In this article, we use Eclipse with M2Eclipse to manage our Maven Project.
- M2Eclipse: a Maven management Eclipse plugin.
- RunJettyRun: a simple Jetty Server which is very easy to use with M2Eclipse.
Demo Project Setup
If you are a Maven user and have already cloned my repository, this project is a Maven project, you can use Eclipse Import Existing Project function to import it. If you get the code by downloading the zip file, unpack it, and put them to your preferred project type.
The Demo Application
The demo application of this article is the same one in ZK Essentials; it is an online order management system.
This application consists of 4 parts:
- Login, when the user requests the URL: http://localhost:8080/springZkDemo for the first time, the request will be redirected to the login page and ask user to perform the login process.
- Product view, after logging-in, user will be redirected back to main page which has three fragments, the first one is product view which displays all available products.
- Shopping cart view, At the east part of main view which displays user's shopping cart content, user can add, remove and change quantity of products in cart.
- Order view, The bottom of the main page displays user's order and order details of each order.
Entity Beans
In this demo application we have several entity beans under package org.zkoss.springdemo.bean
Product
, a product with several properties like name, price, quantityCartItem
, a shopping cart item which references to aProduct
.Order
, When a users submit all items in a shopping cart, we create an order.OrderItem
, an item that comes from submittedCartItem
User
, a user who logins into the application.
Using Spring in ZK Application
Just like a normal Spring web application, you have to configure some listener, context parameter and filter in WEB-INF/web.xml to make Spring manage its application context properly for different levels of scope.
The configuration looks like this: web.xml
<!-- Declares where the Spring application context setting is -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/applicationContext.xml</param-value>
</context-param>
<!-- Spring Context Initialization & Request scope Handling -->
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Our demo project uses the classpath scanning feature to register beans. It detects classes in specified packages with stereotype annotations (e.g. @Component
) and registers them as Spring managed beans.
applicationContext.xml
<!-- AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor are both
included implicitly-->
<context:component-scan base-package="org.zkoss.springdemo" />
<!-- JPA and transaction manager configuration -->
...
Persistence Layer
In our spring configuration, we have declared class path scanning element which implicitly registers PersistenceAnnotationBeanPostProcessor
for us and a transaction manager. Spring can understand @PersistenceContext
both at field and method level and inject proper EntityManager
in current transaction.
Implement DAO on JPA
In our project, we implement DAO (Data Access Object) pattern for the persistence layer with injected EntityManger and this DAO doesn't depends on any Spring's classes.
@Repository
public class ProductDAO {
@PersistenceContext
private EntityManager em;
public List<Product> findAll() {
Query query = em.createQuery("from products");
List<Product> products = query.getResultList();
return products;
}
...
}
Service Layer
Service Layer is composed of business objects which are put in package org.zkoss.springdemo.service
.
Business Object Design practice
In this demo application, we have several main business objects which provides a set of logical API to interact with view. They are:
- org.zkoss.springdemo.service.ProductManager
- org.zkoss.springdemo.service.UserOrderManager
- org.zkoss.springdemo.service.UserCredentialManager
- org.zkoss.springdemo.service.ShoppingCartManager
Let's use them to see how to design your business object in some common scenario.
Singleton Scope Practice
If a business object is stateless, we can declare it as a singleton bean. For example, org.zkoss.springdemo.service.ProductManager, it only provides a list of available products and do not save any user's or product's state. Therefore, we should declare it as a singleton bean.
@Service
public class ProductManager {
@Autowired
private ProductDAO productDao;
public List<Product> findAllAvailable() {
return productDao.findAllAvailable();
}
}
Session Scope Practice
A business object which needs to keep state across multiple requests for individual user should be "session" scope. For example, ShoppingCartManager
contains selected products of a user.
@Component("shoppingCart")
@Scope("session")
public class ShoppingCartManager implements ShoppingCart {
...
}
The UserCredentialManager
keeps the user credential. So we should make them "session" scope to keep data.
@Component
@Scope("session")
public class UserCredentialManager implements Serializable{
private User user;
...
}
Presentation Layer
This layer is about how to use BO in ZK's view, the java classes that controlling zk views are all under org.zkoss.springdemo.controller. In this application we demonstrate how to use Spring bean in ZK MVC controller:
- org.zkoss.springdemo.controller.LoginViewCtrl
- org.zkoss.springdemo.controller.ProductViewCtrl
and how to use Spring bean in ZK MVVM view model bean:
- org.zkoss.springdemo.controller.OrderViewViewModel
- org.zkoss.springdemo.controller.ShoppingCartViewModel
Context Injection in ZK
Adopting Spring's context to ZK view context is very simple, you simply use ZK's org.zkoss.zkplus.spring.DelegatingVariableResolver. Here we will discuss several circumstances in which you can use ZK's Spring DelegatingVariableResolver.
ZK's Listener
In the login process, we have a WorkbenchInit declared in index.zul. It's one of ZK's listeners that allow you to intercept ZK's life cycle. This listener calls UserCredentialManager.isAuthenticated()
to verify user's authentication.
So we need to get Spring managed bean in ZK's listener first. To get Spring managed bean from them, the easiest way is to use SpringUtil.getBean("userCredentialManager")
.
public class WorkbenchInit implements Initiator {
private UserCredentialManager userCredentialManager;
public void doInit(Page page, @SuppressWarnings("rawtypes") Map arg) throws Exception {
if(userCredentialManager==null){
userCredentialManager = (UserCredentialManager)SpringUtil.getBean("userCredentialManager");
}
if (!userCredentialManager.isAuthenticated()) {
Executions.getCurrent().sendRedirect("login.zul");
}
}
//...
In Listener scenario we use a programmatic approach to get managed bean. In ZK MVC controller, we can use annotations for variable wiring to save coding effort. For example, in org.zkoss.spring demo.controller.LoginViewCtrl:
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class LoginViewCtrl extends SelectorComposer<Window> {
@Wire
private Textbox nameTxb, passwordTxb;
@Wire
private Label mesgLbl;
@WireVariable
private UserCredentialManager userCredentialManager;
@Listen("onClick=#confirmBtn; onOK=#passwordTxb")
public void doLogin() {
userCredentialManager.login(nameTxb.getValue(), passwordTxb.getValue());
if (userCredentialManager.isAuthenticated()) {
Executions.getCurrent().sendRedirect("index.zul");
} else {
mesgLbl.setValue("Your User Name or Password is invalid!");
}
}
//...
- Line 1: We use
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
to annotateLoginViewCtrl
which tells super class SelectorComposer that this controller will based on Spring context to do variable wiring. - Line 9: We can use
@WireVariable
to wire UserCredentialManager like Spring's@Autowired
. As you can see, by default if the field's name is the name of that Spring bean, the instance will be wired automatically.
ZK MVVM
In ZK MVVM, the way to do variable wiring is very similar to ZK MVC, let's use org.zkoss.springdemo.controller.OrderViewViewModel for example:
@VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
public class OrderViewViewModel {
@WireVariable
private UserOrderManager userOrderManager;
private Order selectedItem;
public Order getSelectedItem() {
return selectedItem;
}
@NotifyChange("selectedItem")
public void setSelectedItem(Order selectedItem) {
this.selectedItem = selectedItem;
}
public List<Order> getOrders() {
return userOrderManager.findAll();
}
@Command
@NotifyChange({"orders", "selectedItem"})
public void cancelOrder() {
if (getSelectedItem() == null) {
return;
}
userOrderManager.cancelOrder(getSelectedItem());
setSelectedItem(null);
}
@GlobalCommand
@NotifyChange("orders")
public void submitNewOrder(
@BindingParam("cartItems")List<CartItem> cartItems
,@BindingParam("orderNote") String orderNote){
userOrderManager.createOrder( cartItems, orderNote);
}
}
- Line 1,4: We reuse @VariableResolver and @WireVariable annotations here, which makes our View Model object becomes very clean to both ZK view and data.
Conclusion
In this series of articles(with the other two: Practices Of Using CDI In ZK, Starting A Web Application Based On ZK CDI JPA and Jetty ) I showed how to start a web application stack based on two of the most famous application frameworks(CDI, Spring) with ZK. As you can see, the usage of these framework's context are totally the same in ZK.
References
- ↑ Practices Of Using CDI In ZK
- ↑ Visit github smalltalk and click "Download as zip" button or use a git client to clone the whole repository.
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |