本文使用的開發包為:jsf 1.2_04+hibernate 3.2.2.ga+spring 2.0.5,開發環境為jdk 1.5+myeclipse 6.0+mysql 5.0。
一、三層設計
本文實現的功能雖然簡單,但是仍然采用了三層的設計:
1)數據訪問層:本文使用Hibernate實現數據訪問。
2)業務邏輯層:本文使用Spring組織業務邏輯。
3)表示層,本文使用JSF進行前台顯示。
分層的好處在於:有利於系統的擴展性、伸縮性和維護性。
下面讓我們演示每一層具體是如何做的,在開始之前,首先看一下我們的數據庫設計:
實現登錄功能,需要一張userinfo表,包含id, username和password字段。
1. logon.sql:#
# Database structure for database 'logon'
#
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `logon` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE logon;
#
# Table structure for table 'userinfo'
#
CREATE TABLE /*!32312 IF NOT EXISTS*/ `userinfo` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(100) NOT NULL default '',
`password` varchar(100) NOT NULL default '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#
# Dumping data for table 'userinfo'
#
/*!40000 ALTER TABLE userinfo DISABLE KEYS;*/
LOCK TABLES userinfo WRITE;
REPLACE INTO userinfo (id, username, password) VALUES (1,'admin','admin');
UNLOCK TABLES;
/*!40000 ALTER TABLE userinfo ENABLE KEYS;*/
好了,讓我們開始吧!
二、數據訪問層
因為只有一張userinfo表,所以數據訪問部分也不復雜。
首先,我們定義了一個IUserDAO接口,用於根據用戶名得到該用戶信息。
2. com.it168.logon.model.dao.IUserDAO.java:
package com.it168.logon.model.dao;
import com.it168.logon.model.businessobject.Userinfo;
public interface IUserDAO {
public Userinfo getUser(String username);
}
接著,UserDAO類實現了IUserDAO接口:
3. com.it168.logon.model.dao.impl.UserDAO.java:
package com.it168.logon.model.dao.impl;
import java.util.Iterator;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.it168.logon.model.businessobject.Userinfo;
import com.it168.logon.model.dao.IUserDAO;
public class UserDAO extends HibernateDaoSupport implements IUserDAO {
public UserDAO() {
super();
}
@SuppressWarnings("unchecked")
public Userinfo getUser(String username) {
Userinfo user = null;
List userList = getHibernateTemplate().findByNamedParam(
"from Userinfo u where u.username = :username", "username",
username);
if (userList != null) {
Iterator i = userList.iterator();
while (i.hasNext()) {
user = (Userinfo) i.next();
}
}
return user;
}
}
大家可以看到,UserDAO類繼承了HibernateDaoSupport類,HibernateDaoSupport類是Spring中的,該類極大地簡化了數據持久化操作。
大家或許注意到:
from Userinfo u where u.username = :username
這和我們平時使用的SQL語句很相似,但這是HQL,它針對的不是表,而是類/對象。既然HQL中使用了Userinfo類,那麼Userinfo類又包括哪些內容呢?
4. com.it168.logon.model.businessobject.Userinfo.java:
package com.it168.logon.model.businessobject;
@SuppressWarnings("serial")
public class Userinfo implements java.io.Serializable {
// Fields
private Integer id;
private String username;
private String password;
// Constructors
/** default constructor */
public Userinfo() {
}
/** full constructor */
public Userinfo(String username, String password) {
this.username = username;
this.password = password;
}
// Property accessors
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
大家可以看到,Userinfo類並沒有什麼神奇的地方,只是一個簡單的POJO類。但是,為什麼這樣一個簡單的POJO類可以和數據庫查詢關聯起來呢?
5. com.it168.logon.model.businessobject.Userinfo.hbm.xml:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.it168.logon.model.businessobject.Userinfo" table="userinfo">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="increment" />
</id>
<property name="username" type="java.lang.String">
<column name="username" length="100" not-null="true" />
</property>
<property name="password" type="java.lang.String">
<column name="password" length="100" not-null="true" />
</property>
</class>
</hibernate-mapping>
這就是原因所在!Userinfo.hbm.xml中定義了Userinfo類中屬性與Userinfo表中字段的關系。而Hibernate也這是借此實現了ORM映射。
我們的數據庫信息呢?別擔心,在這裡:
6. applicationContext.xml代碼片斷一:
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="jdbcUrl">
<value>jdbc:mysql://localhost:3306/logon</value>
</property>
<property name="user">
<value>root</value>
</property>
<property name="password">
<value>123456</value>
</property>
<property name="minPoolSize">
<value>5</value>
</property>
<property name="maxPoolSize">
<value>20</value>
</property>
<property name="maxIdleTime">
<value>300</value>
</property>
<property name="maxStatements">
<value>50</value>
</property>
<property name="idleConnectionTestPeriod">
<value>3000</value>
</property>
</bean>
三、業務邏輯層
為了實現登錄功能,這裡只需要一個簡單的登錄邏輯。首先,我們定義了一個IUserService接口,用於根據用戶名和密碼得到用戶信息。
7. com.it168.logon.model.service.IUserService.java:
package com.it168.logon.model.service;
import com.it168.logon.model.businessobject.Userinfo;
public interface IUserService {
public Userinfo login(String username, String password);
}
接著,UserService類實現了該接口:
8. com.it168.logon.model.service.impl.UserService.java:
package com.it168.logon.model.service.impl;
import com.it168.logon.model.businessobject.Userinfo;
import com.it168.logon.model.dao.impl.UserDAO;
import com.it168.logon.model.service.IUserService;
public class UserService implements IUserService {
private UserDAO userDAO;
public void setUserDAO(UserDAO userDAO) {
this.userDAO = userDAO;
}
public Userinfo login(String username, String password) {
Userinfo user = this.userDAO.getUser(username);
if (user != null) {
if (!user.getPassword().equals(password)) {
user = null;
}
}
return user;
}
}
大家或許注意到了這樣一句:
private UserDAO userDAO;
這裡的UserDAO正是數據訪問層中的UserDAO。在UserService類中,我們沒有看到UserDAO的實例化過程,既然沒有實例化,Userinfo user = this.userDAO.getUser(username);這行代碼,又是如何發揮作用的呢?
9. applicationContext.xml代碼片斷二:
<bean id="userDAO" class="com.it168.logon.model.dao.impl.UserDAO">
<property name="hibernateTemplate">
<ref bean="hibernateTemplate" />
</property>
</bean>
<bean id="userService"
class="com.it168.logon.model.service.impl.UserService">
<property name="userDAO">
<ref local="userDAO" />
</property>
</bean>
這也正是Spring的強大所在!它將類的實例化過程交給了Spring容器,從而實現了控制反轉(IoC)。
四、表示層
我們實現了業務邏輯,接下來就看業務邏輯是如何與JSF頁面相關聯的。
首先我們定義了一個UserBean類,該類實現了一個verify()方法,如果登錄成功,返回“success”字符串,如果失敗,則返回“failure”字符串。
10. com.it168.logon.view.bean.UserBean.java代碼片斷:
public String verify() {
Userinfo user = serviceLocator.getUserService().login(this.username,
this.password);
if (user == null) {
this.errorMessage = "登錄失敗";
this.loggedIn = false;
return "failure";
} else {
this.loggedIn = true;
return "success";
}
}
大家可能注意到了這段代碼:
Userinfo user = serviceLocator.getUserService().login(this.username, this.password);
這裡的serviceLocator是ServiceLocatorBean類的實例:
11. com.it168.logon.view.bean.ServiceLocatorBean.java:
package com.it168.logon.view.bean;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.it168.logon.model.service.IUserService;
import com.it168.logon.model.service.impl.UserService;
import com.it168.logon.view.servicelocator.IServiceLocator;
public class ServiceLocatorBean implements IServiceLocator {
private static final String USER_SERVICE_BEAN_NAME = "userService";
private ApplicationContext appContext;
private UserService userService;
public ServiceLocatorBean() {
ServletContext context = (ServletContext) FacesContext
.getCurrentInstance().getExternalContext().getContext();
this.appContext = WebApplicationContextUtils
.getRequiredWebApplicationContext(context);
this.userService = (UserService) this
.lookupService(USER_SERVICE_BEAN_NAME);
}
public IUserService getUserService() {
return this.userService;
}
public Object lookupService(String serviceBeanName) {
return appContext.getBean(serviceBeanName);
}
}
ServiceLocatorBean類實現了業務邏輯與JSF頁面上下文的關聯。
現在來看一下JSF頁面如何實現了與UserBean的綁定:
12. login.jsp代碼片斷:
<f:view>
<h:form rendered="true">
<div align="center">
<h:outputText escape="false" rendered="true"
value="#{userBean.errorMessage}"></h:outputText>
</div>
<div align="center">
<h:outputText value="用戶名: " />
<h:inputText id="username" required="true"
value="#{userBean.username}">
</h:inputText>
<h:message for="username" />
<br>
</div>
<div align="center">
<h:outputText value="密 碼: " />
<h:inputSecret id="password" required="true" rendered="true"
value="#{userBean.password}" style="width: 154px">
</h:inputSecret>
<h:message for="password" />
<br>
</div>
<br>
<div align="center">
<h:commandButton rendered="true" value="登錄"
action="#{userBean.verify}"></h:commandButton>
</div>
</h:form>
</f:view>
其中,“userBean”從何而來呢?
13. faces-config.xml代碼片斷一:
<managed-bean>
<managed-bean-name>userBean</managed-bean-name>
<managed-bean-class>
com.it168.logon.view.bean.UserBean
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>serviceLocator</property-name>
<value>#{serviceLocatorBean}</value>
</managed-property>
</managed-bean>
在faces-config.xml文件中的<managed-bean>節點中,我們定義了“userBean”對應的類。
14. faces-config.xml代碼片斷二:
<navigation-rule>
<from-view-id>/pages/login.jsp</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/pages/welcome.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/pages/login.jsp</to-view-id>
</navigation-case>
</navigation-rule>
在faces-config.xml文件中的<navigation-rule>節點中,我們定義了頁面導航規則。
到目前為止,我們介紹了Hibernate、Spring和JSF如何在三層設計中發揮作用的,現在我們可以看一下實際效果,訪問http://localhost:8080/Logon:
(圖1)
如果登錄成功:
(圖2)
登錄失敗,則: