实例解析Struts 2.1与Sping、Hibernate集成
Spring本身提供了一套极其优秀的MVC框架,但这套框架的设计过于复杂,采用了大量的映射策略,Struts 2是以Webwork 2作为基础发展出来。而在Webwork 2.2之前的Webwork版本,其自身有一套控制反转的实现,Webwork 2.2在Spring 框架的如火如荼发展的背景下,决定放弃控制反转功能的开发,转由Spring实现。因此,Struts 2推荐通过Spring实现控制反转。
13.4.1 配置应用项目
首先用Myeclipse开发向导创建一个Web应用项目,在本例中创建的项目名为sshapp,然后把下载的struts-2.1.6-all.zip解压包中lib目录中的struts2-spring-plugin-2.1.6.jar、commons-logging.jar,freemarker.jar,ognl.jar,struts2-core.jar,xwork.jar, commons-io-1.3.2.jar ,commons-fileupload-1.2.1.jar (lib目录中Struts 2框架的类库有版本后缀。例如commons-logging.jar,可能是commons-logging-1.0.4.jar;struts2-core.jar可能是struts2-core-2.1.6.jar),加入到项目的WEB-INF/lib目录中,这里需要确保这些包的版本一致,在struts-2.1.6-all.zip解压包中的lib目录中存放的是spring-core-2.0.8.jar包,并没有spring-core-2.5.3.jar,如果用spring-core-2.0.8.jar包,启动Tomcat6时会出现“java.lang.ClassNotFoundException: org.springframework.core.SmartClassLoader”错误提示,可以从struts-2.1.6-all.zip包的apps目录中的struts2-showcase-2.1.6.war包中的WEB-INF/lib目录中找到spring-core-2.5.3.jar包。接下来需要修改WEB-INF/web.xml文件内容,添加内容如下:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class > </listener> |
上面的配置主要是加入Spring的ContextLoaderListener监听器,方便Spring与Web容器交互。紧接着修改Struts.properties文件,以便Struts 2运行时使用Spring来创建对象(如Action等),添加内容如下:
struts.objectFactory =spring |
13.4.2 创建实体表
在MySQL中先创建一个数据库,名称为hibernatedb,然后创建用户表user,创建表的SQL内容如清单13-32所示。
清单13-32
CREATE TABLE hibernatedb.user ( id int(10) NOT NULL auto_increment, name varchar(50) NOT NULL , password varchar(16) NOT NULL , PRIMARY KEY (id) ); |
13.4.3 创建Hibernate框架
在本例中,以前面创建的sshapp项目为基础,来创建Hibernate应用。首先在MyEclipse中选中sshapp项目,然后单击右键,在弹出的菜单中选择MyEclipse项下的项Add Hibernate Capabilites项,弹出如图13-5所示界面,在本例中,选择的Hibernate版本是3.2,其他选择MyEclipse默认的值,然后单击Next按钮弹出如13-6所示按钮,在这里将创建hibernate.cfg.xml配置文件。 MyEclipse中,设置数据库的连接,并将所需的MySQL的jdbc类库拷贝到lib目录中。然后使用MyEclipse的数据Database Explorer向导工具创建User.hbm.xml、User.java映射文件。User.java代码清单13-33所示。
清单13-33
public class User extends AbstractUser implements java.io.Serializable { // Fields private Integer id; private String name; private String password; // Constructors public User() { } // Property accessors public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } } |
User.hbm.xml内容如清单13-34所示。
清单13-34
<?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.hibernate.bean.User" table="user" catalog="hibernatedb"> <id name="id" type="java.lang.Integer"> <column name="id" /> <generator class="identity" /> </id> <property name="name" type="java.lang.String"> <column name="name" length="50" not-null="true" /> </property> <property name="password" type="java.lang.String"> <column name="password" length="16" not-null="true" /> </property> </class> </hibernate-mapping> |
生成的hibernate.cfg.xml配置文件内容如清单13-35所示。
清单13-35
<?xml version="1.0" encoding="gb2312"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- 显示实际操作数据库时的SQL --> <property name="show_sql">true</property> <property name="format_sql">false</property> <property name="cache.use_query_cache">false</property> <!-- SQL方言,这边设定的是MySQL --> <!-- JDBC驱动程序 --> <property name="dialect"> org.hibernate.dialect.MySQLDialect</property> <property name="connection.driver_class"> com.mysql.jdbc.Driver</property> <property name="connection.url"> jdbc:mysql://localhost/hibernatedb </property> <property name="connection.username">root</property> <property name="connection.password">12345</property> <!-- 对象与数据库表格映像文件 --> <mapping resource="ch11/hibernate/User.hbm.xml" /> </session-factory> </hibernate-configuration> |
图13-5 导入Hibernate库 图13-6 创建Hibernate配置
13.4.4 创建DAO
UserDAO接口程序如清单13-36所示。
清单13-36
package com.hibernate.dao; import com.hibernate.bean.User; public interface UserDAO { public abstract boolean isValidUser(String username,String password); public void save(User user); public void delete(User user); public User findById(int id); } |
在创建Hibernate3.2 + Spring2.5+Struts2.1应用程序时,需要相应的类库,应用MyEclipse开发时,可通过开发向导导入所需要的包,具体方法在新建的项目上,单击右键,在弹出的菜单中选择Properties项,弹出如图13-7所示对话框。在对话框中选择Java Build Path项。在右边选择第三个Tab项Libraries,列出了当前项目己添加的类库,要添加MyEclipse中的类库,单击Add Library按钮,弹出如图13-8 所示对话框。在弹出的对话框中选择MyEclipse Libraties项,单击Next按钮,进入MyEclipse Libratis库的选择界面,如图13-9所示。本例选择的类库有Spring 2.5 AOP Libraties、Spring 2.5 Core Libraties、Spring2.0 Persistence Core Libraties和Spring 2.5 Web Libraties。
图13-7 类库配置页面
图13-8 MyEclipse类库 图13-9 选择类库
作者提示:在整合Sping2.5+Struts2.1 +Hibernate3.2中,在启动服务器时会出现java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit导常,这是由于Spring 2.5和 Hibernate 3.2共用的一些 jar 文件发生了版本冲突。解决的办法是在MyEclipse的主菜单Window→Preferences→Myeclipse→Project项中,选择Capabilities→Spring项,从lib中删除asm-2.2.3.jar包即可。如果项目的WEB-INF/lib目录下有asm-2.2.3.jar包,则需要删除该包。
UserDAO接口实现类UserDAOImp代码如清单13-37所示。
清单13-37
package com.hibernate.dao; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import com.hibernate.bean.User; import java.io.Serializable; import java.util.List; import org.hibernate.Query; import org.hibernate.SessionFactory; public class UserDAOImp extends HibernateDaoSupport implements UserDAO { private SessionFactory sessionFactory; private static String hql = "select u from User u where u.name=? and password=? "; public boolean isValidUser(String name, String password) {// 验证登录 String para[] = new String[2]; para[0] = name; para[1] = password; Query query = this.getHibernateTemplate().getSessionFactory() .openSession().createQuery(hql); query.setParameter(0, para[0]); query.setParameter(1, para[1]); List userList = query.list(); try { if (userList.isEmpty()) { return false; } } catch (Exception ex) { ex.printStackTrace(); } return true; } public void save(User user) {// 保存 this.getHibernateTemplate().save(user); } public void delete(User user) {// 删除用户 this.getHibernateTemplate().delete(user); } public User findById(Serializable id) {// 根据ID查找 return (User) this.getHibernateTemplate().get(User.class, id); } public User findById(int id) { return (User) this.getHibernateTemplate().get(User.class, id); } } |
13.4.4 创建Action
接下来修改LoginAction.java代码,使用UserDAO的方法来进行用户验证,代码如清单13-38所示。
清单13-38
package com.struts2.action; import com.hibernate.dao.UserDAO; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private static final long serialVersionUID = 1L; private UserDAO userDAO; // 依赖于Spring注入 private String name; private String password; public UserDAO getUserDAO() { return userDAO; } public void setUserDAO(UserDAO userDAO) { this.userDAO = userDAO; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } // 处理用户请求的execute方法 public String execute() throws Exception { System.out.println("执行操作");
if (userDAO.isValidUser(this.getName(), this.getPassword())) { ActionContext.getContext().getSession().put("user", getName()); return this.SUCCESS; } else return this.ERROR;
} // 完成输入校验需要重写的validate方法 public void validate() {// 如果用户名为空,或者用户名为空字符串 System.out.println("校验输入"); if (getName() == null || getName().trim().equals("")) {// 添加表单校验错误 addFieldError("name", getText("user.required")); }// 当密码为空,或者密码为空字符串时,添加表单校验错误 if (getPassword() == null || getPassword().trim().equals("")) { addFieldError("pass", getText("pass.required")); } } } |
上面定义的LoginAction类使用属性(Getter/Setter)注入方法取得userDAO对象。
13.4.5 创建Spring应用
接下来创建Spring的配置文件applicationContext.xml,把该文件存放在项目的WEB-INF目录下,配置内容如清单13-39所示。
清单13-39
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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-2.1.xsd"> <!-- Hibernate设置 --> <!-- 会话工厂设置,读取Hibernate数据库配置信息 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation"> <value>classpath:hibernate.cfg.xml</value> <!--直接引入Hibernate配置方法 --> </property> </bean> <!-- Spring设置 --> <!-- 会话模板 --> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref local="sessionFactory" /> </property> </bean> <!-- 配置UserDAOImpl持久化类的DAO bean--> <bean id="userDAO" class="com.hibernate.dao.UserDAOImp"> <!--采用依赖注入来传入SessionFactory的引用--> <property name="sessionFactory"> <ref local="sessionFactory" /> </property> </bean> <!-- 配置事务拦截器--> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <!-- 事务拦截器bean 需要依赖注入一个事务管理器--> <property name="transactionManager"> <ref local="transactionManager" /> </property> <property name="transactionAttributes"> <!-- 下面定义事务传播属性--> <props> <prop key="save">PROPAGATION_REQUIRED</prop> <prop key="dele*">PROPAGATION_REQUIRED</prop>
</props> </property> </bean> <!--定义BeanNameAutoProxyCreator ,该bean 是个bean 后处理器,无须被引用,因此 没有id 属性,这个bean 后处理器,根据事务拦截器为目标bean 自动创建事务代理--> <bean class ="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!--指定对满足哪些bean name 的bean 自动生成业务代理--> <property name="beanNames"> <!-- 下面是所有需要自动创建事务代理的bean--> <list> <value>userDAO</value> </list><!-- 此处可增加其他需要自动创建事务代理的bean--> </property> <!--下面定义BeanNameAutoProxyCreator所需的事务拦截器--> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean> <!-- 设置代理Struts动作 --> <bean name="loginAction" class="com.struts2.action.LoginAction" scope="prototype"> <property name ="userDAO"> <ref local ="userDAO" /> </property> </bean> </beans> |
在上面的配置中,Struts 2会为每一个请求创建一个Action对象,所以在定义LoginAction时应用scope="prototype"属性。这样Spring会为每个调用者返回一个新的LoginAction对象。 因为userDAO被配置为默认的scope(也即是singleton,单根模式),所以在实现时应保证其线程安全。
上面定义的Action在struts.xml中的配置内容如清单13-41所示。
清单13-41
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <include file="struts-default.xml" /> <package name="Spring" extends="struts-default"> <action name="loginaction" class="loginAction"> <result name="success">/index.jsp</result> <result name="error">/sshlogin.jsp</result> <result name="input">/sshlogin.jsp</result> </action> </package> </struts> |
这里的Action 配置与前面不同的就是class属性,它对应于Spring所定义的bean的id,而不是它的类全名。
13.4.6 测试部署应用
最后,定义一个显示用户列表的JSP文件sshlogin.jsp,实现代码如清单13-42所示。
清单13-42
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%> <html> <head> </head> <body> <!-- 提交请求参数的表单 --> <form action="loginaction.action" method="post" > <table align="center"> <caption> <h4>用户登录信息</h4> </caption> <tr><!-- 用户名的表单域 --> <td>用户名:<input type="text" name="name" /></td></tr> <tr><td>密 码 : <input type="password" name="password" style=" width : 149px;"/> </td></tr> <tr align="center"> <td colspan="2"> <input type="submit" value="登 录" /> <input type="reset" value="清 除" /> </td></tr> </table> </form> </body> </html> |
发布应用程序后,在浏览器地址栏中输入http://localhost:8081/sshapp/sshlogin.jsp,程序运行页面如图13-10所示。为了测试登录功能,可以先在前面创建的User表中录入一些用户信息,然后在如图13-10所示登录页面中测试。
图13-10 登录页面
上面的应用程序介绍了一个简单的登录程序,演示如何整合Spring2.5、Hibernate3.2、Struts2.1三种技术。在本例中,应用Spring2.5作为整个系统的控制容器,Struts2.1与Hibernate3.2在Spring2.5的控制容器中工作。