技术开发 频道

整合JSF、Hibernate、Spring实现用户登录应用



【IT168 专稿】

    摘要:在一个系统中,用户登录功能是最常见的功能之一。本文演示了如何使用JSF+Hibernate+Spring组合实现简单的用户登录功能。
 
本文使用的开发包为: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)
登录失败,则:




0
相关文章