??xml version="1.0" encoding="utf-8" standalone="yes"?>
W一?struts与spring的融?/font>
W一步:配置环境与技术支?/font>
1、环境:tomcat5.0 + eclipse3.2.2 + myEclipse5.5 + jdk1.5
2、技术:struts1.1+spring2.0
分析Q经q多ơ实验,Q初建struts+springQ项目中出现的问题和工具及技术版本没有根本关p,只要在(其他目q行Q已l配|成功的环境下运行就好。这里要注意的是QmyEclipse5.0以下的版本不支持spring2.0。小提C:本h初次在该环境下操作时Q多ơ不成功Q最后从新安装配|环境后Qstruts+spring目才正常运行,疑与myEclipse有关?/font>
W二步:新徏工程SSHProject
1、新建工E?/font>
分析Q不同版本的eclipse新徏目对话框不同,3.2.2以下版本没有java EE 5.0。这里我们选择J2EE1.4Q?/font>
2、导入struts?/font>
3、导入spring?/font>
分析Q?/strong>q里我们使用的是spring2.0Q如果你的版本不支持2.0Q就使用spring1.2版本。spring1.2与struts1.1同样兼容Q但spring1.2只支持hibernate3.0以下的版本。如果你选择spring1.2Q就不得不用hibernate3.0或?.1。小提C:对于初学者来_最好把所有的spring包导入项目。提醒:applicationContext.xml我们攑֜src下,但我们要知道~译部v后,默认在classes文g夹,所以我们在以后的配|中Q一定要注意路径问题?/font>
3、新建com.ssh.beans.po和com.ssh.beans.dao两个包已做备用?/font>
好了Q工E框架已l搭好,现在我们可以往里面放东西了?/font>
W三?创徏action和bean
1、在com.ssh.beans.po中创建Customer.java。内容如下:
package com.ssh.beans.po;
public class Customer {
String custId;
String custName;
public Customer(){
}
public Customer(String custId,String custName){
this.custId=custId;
this.custName=custName;
}
public void setCustId(String custId){
this.custId=custId;
}
public String getCustId(){
return this.custId;
}
public void setCustName(String custName){
this.custName=custName;
}
public String getCustName(){
return this.custName;
}
}
2、在com.ssh.beans.dao中创建CustomerDAO.java及其接口ICustomerDAO.java。内容如下:
package com.ssh.beans.dao;
import java.util.ArrayList;
import java.util.List;
import com.ssh.beans.po.Customer;
public class CustomerDAO implements ICustomerDAO {
public List getALLCustomer(){
List list=new ArrayList();
Customer c1=new Customer("1","zhang");
Customer c2=new Customer("2","xiaoling");
list.add(c1);
list.add(c2);
return list;
}
}
package com.ssh.beans.dao;
import java.util.List;
public interface ICustomerDAO {
public List getALLCustomer();
}
3、创建CustomerAction.java
分析Q这里action的创Z我们以前创徏action一P但我们要注意的是Q你导入什么版本的strutsp生成什么版本的actionQ这里是struts1.1?/font>
接下来我们看看struts-config.xml里面的内容:
<?xml version="1.0" encoding="UTF-8"?> <struts-config> </action-mappings> <message-resources parameter="com.ssh.struts.ApplicationResources" /> 没错Q内容和我们以前的东东一栗?/p>
我们再给CustomerAction.java一些内容: package com.ssh.struts.action; import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; import com.ssh.beans.dao.CustomerDAO; public class CustomerAction extends Action { 好的Q我们现在测试一下!如果讉Khttp://localhost:8080/SSHProject/customer.do能顺利进入index.jsp面Qƈ输出用户custName|说明以上我们的工作是正确的! W四?配置stuts-config.xml和applicationContext.xml文g 看到q里Q大家可能会认ؓQ这和以前的web工程的创建没有什么两P和struts与spring融合没有什么关p,不用着急,奥妙在sturts-config.xml与applicationContext.xml文g配置中?/p>
1、配|stuts-config.xml文g 在这里,我们要做两个工作Q第一Q将CustomerAction替换成DelegatingActionProxy代理。第二,d代理插gContextLoaderPlugIn。修改后的内容如下: <?xml version="1.0" encoding="UTF-8"?> <struts-config> <message-resources parameter="com.ssh.struts.ApplicationResources" /> 注:_体字ؓ修改的内?/p>
分析Q?/strong>你一定要做到Q?、确保spring-struts.jar导入目?、保证applicationContext.xml文g路径正确。我们在导入spring包时提刎ͼapplicationContext.xml文g攑֜src下,~译部v后,文g默认存放在WEB-INF/classes下。所以上面的配置为:value="/WEB-INF/classes/applicationContext.xml"/>?/font> 2、配|applicationContext.xml 在这个文件中Q我们的d是加入我们需要的bean。到目前为止Q我们要加入两个beanQ即QCustomerDAO与CustomerAction。修改后的内容ؓQ?/p>
<?xml version="1.0" encoding="UTF-8"?> </beans> 注:_体字ؓd的内?/p>
分析Q?/strong>在这个文件中你要保Q第一QCustomerAction bean?lt;bean>标签的属性name?name="/customer")一定与struts-config.xml中属性path的?path="/customer")一致。第二?lt;property>标签的属性name值在CustomerAction中一定有对应的属性。第三?lt;ref>标签的属性beang定与CustomerDAO bean?lt;bean>标签的属性nameg?注:q就是注入目标对?。第四、CustomerDAO bean?lt;bean>标签的属性classg定是一个实现类(不能为接??/p>
3、修改CustomerAction.java q一步很单,只要?strong>setCustomerDAO(new CustomerDAO()); 好的Q到现在为止Q我们已l对struts和springq行了融合。再来测试一下,如果输入同样的内容,OKQ?/p>
如果以上操作大家都没有问题,我们向下看。来吧,一赯合spring与hibernate W二?Spring与Hibernate的融?/strong> 有的朋友可能只希望知道spring与hibernate的融合。所以在讲struts+spring+hibernate之前Q我们蟩q用stutsQ先说一下spring+hibernate的融合。如果仍然用SSHProjectq个目Q需要把po和dao包下面的cd除,因ؓ我们在生成媄文件和DAO时可能会出现重名文g。还有applicationContext.xml中的bean同样删除?/p>
W一?配置数据环境 既然我们用到hibernateQ就要给他配|数据库Q这里我们用的是mysql5.0。eclipse3.2.2一下的版本?.2.2版本数据库的配置是不同的Q这里我们只?.2.2的配|?/font> 1、打开DB Brower 2、新建数据库q接 在DB Brower中右?gt;新徏打开下面对话框,选择输入正确的配|。提C:注意你的数据库名、密码、和驱动器?/font> 单击“完成”Q测试如果连接到数据库,OK?/p>
W二?选择hibernate与包的导?/strong> 1、hibernate的选择 上面我们已经提到Qspring1.2只支持hibernate3.0以下的版本,所以如果你选择的是spring1.2一定要注意q个问题。这里我使用的是hibernate3.1 2、包的导?/strong> 单击“完成”OK?/p>
分析Q?/strong>在导入包时出现几个问题:1、在找spring建好的applicationContext.xml文g时没有找到\径,被迫把其他项目的数据库连接bean考到该文件内((注:仅仅是我存在的问??、把自动生成的sessionFactory删除?、最后可能会出现找不到包Q所以你要手动添加几个包?/p>
现在我们看看applicationContext.xml文g内容有什么变化,内容如下Q?/p>
<?xml version="1.0" encoding="UTF-8"?> </beans> 注:_体字ؓ自动加入的内宏V?/p>
W三?创徏影射文gpo及dao 1、创建媄文?/font> 首先你要定所需要的包都导入classpath路径中,否则在创建媄文件中会出C些不能操作的对象。如果出现问题,大家多重复做几次?/font> 单击"完成"Q就OK?/p>
在包资源理器中我们可以看到Q自动生成四个文Ӟ包括CustomerDAO?/p>
Z方便操作我们包CustomerDAO攑֜dao包下。如果你没有删除ICustomerDAO接口Q那我们׃用它?因ؓspring是面Ҏ口的Q所以我们的操作都应该经q接??/p>
接下来,我们再看看applicationContext.xml有什么变化。内容如下: <?xml version="1.0" encoding="UTF-8"?> 注:_体字ؓ新内宏V提醒:如果你没有改CustomerDAO的\径,它应该在po包下?/p>
2、创建dao?/strong> CustomerDAO.java我们使用hibernate自动生成QICustomerDAO.java接口使用我们以前建好的?/p>
3、创建测试类 既然我们不用actionQ那么我们就新徏一个类Test.java用于试。内容如下: package com.ssh.struts.action; import java.util.List; import org.springframework.context.ApplicationContext; import com.ssh.beans.dao.CustomerDAO; public class Test{ 分析Q?/strong>在测试中可能出现两个异常Q?/p>
异常一、java.lang.NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool。如果出现这个异常说明缺commons-pool-1.2.jar包?/p>
异常二、org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class 'com.mysql.jdbc.Driver';;;Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver。如果出现这个异常,说明在构\径中没有驱动包?/p>
好的Q我们现在测试一下,如果System.out.println("list.size():"+list.size());执行Q说明我们对spring与hibernate的融合成功了?/p>
W三?整合struts+spring+hibernate 我们在上两篇的基只要再对applicationContext.xml文gq行修改Q就可以辑ֈ我们整合的目地?/font> W一?完善applicationContext.xml内容 1、添加事务处理。内容如下: <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 2、CustomerAction Bean注入事务处理。内容如下: <bean name="/customer" class="com.ssh.struts.action.CustomerAction" > 3、最lapplicationContext.xml文gQ内容如下: <?xml version="1.0" encoding="UTF-8"?> W二步,修改CustomerAction 最后内容如下: import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; import com.ssh.beans.dao.ICustomerDAO; W三?解决找不到Action问题 初学者会l常遇到下面q个问题Q?/font> type Status report message Servlet action is not available description The requested resource (Servlet action is not available) is not available. 是找不到我们的action?/p>
当你努力去解册个问题时Q会发现LapplicationContext.xml下面q个<property>标签Q一切正常: <property name="mappingResources"> 那是什么原因呢Q我惛_安会首先想C面两个问题: 1、\径是否正:即com/ssh/beans/po/Customer.hbm.xml的\径正么Q?/p>
2、文件是否正:即Customer.hbm.xml的文件内容对么? 当你费了一w力气发C切OKQ到底什么原因?Q? 问题在于构g路径(lib)内的包重叠(提示Q前提是你要保证q个问题出现之前都正常)Q所以你要确定构\径里的包不能重复Q?/strong> Q?/strong>大家在导入包Ӟ按照默认导入Q不要把所有的包都D工程Q在操作中在把需要的jar包导q去(最好不要把整个liberariesD去)Q这样即可以减小工程的大,又能保struts\spring\hibernate之间的包不会重叠或者被替换?/p>
好了Q我的Q务完成了Q大家删包去吧!你好运Q?/p>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "
<data-sources />
<form-beans />
<global-exceptions />
<global-forwards />
<action-mappings >
<action path="/customer" type="com.ssh.struts.action.CustomerAction">
<forward name="success" path="/index.jsp" />
</action>
</struts-config>
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import com.ssh.beans.dao.ICustomerDAO;
import com.ssh.beans.po.Customer;
ICustomerDAO customerDAO=null;
public void setCustomerDAO(ICustomerDAO customerDAO){
this.customerDAO=customerDAO;
}
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
List list=new ArrayList();
Customer customer=null;
setCustomerDAO(new CustomerDAO());
if(customerDAO!=null){
list=customerDAO.getALLCustomer();
for(int i=0;i<list.size();i++){
customer=(Customer)list.get(i);
System.out.println("OK:"+customer.getCustName());
}
}else{
System.out.println("ERROR or NULL");
}
return mapping.findForward("success");
}
}
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "
<data-sources />
<form-beans />
<global-exceptions />
<global-forwards />
<action-mappings >
<action path="/customer" type="org.springframework.web.struts.DelegatingActionProxy">
<forward name="success" path="/index.jsp" />
</action>
</action-mappings>
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/classes/applicationContext.xml"/>
</plug-in>
</struts-config>
<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.0.xsd">
<bean name="/customer" class="com.ssh.struts.action.CustomerAction" >
<property name="customerDAO"><ref bean="customerDAO"/></property>
</bean>
<bean name="customerDAO" class="com.ssh.beans.dao.CustomerDAO" />
<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.0.xsd">
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url"
value="jdbc:mysql://localhost:3306/pullhand">
</property>
<property name="username" value="root"></property>
<property name="password" value="815241"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
</props>
</property>
</bean>
<beans
xmlns="
xmlns:xsi="
xsi:schemaLocation="http://www.springframework.org/schema/beans
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url"
value="jdbc:mysql://localhost:3306/pullhand">
</property>
<property name="username" value="root"></property>
<property name="password" value="815241"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>com/ssh/beans/po/Customer.hbm.xml</value></list>
</property>
</bean>
<bean id="CustomerDAO" class="com.ssh.beans.dao.CustomerDAO">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
</beans>
import org.springframework.context.support.ClassPathXmlApplicationContext;
private ApplicationContext context;
private void test(){
CustomerDAO customerDAO=(CustomerDAO)context.getBean("customerDAO");
List list=customerDAO.findAll();
if(list!=null&&list.size()>0){
System.out.println("list.size():"+list.size());
}else{
System.out.println("ERROR or NULL");
}
}
private void run(){
context=new ClassPathXmlApplicationContext("applicationContext.xml");
test();
}
public static void main(String[] args){
new Test().run();
}
}
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="customerDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref local="customerDAO" />
</property>
<property name="transactionAttributes">
<props>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<property name="customerDAO"><ref bean="customerDAOProxy"/></property>
</bean>
<beans
xmlns="
xmlns:xsi="
xsi:schemaLocation="http://www.springframework.org/schema/beans
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="com.mysql.jdbc.Driver">
</property>
<property name="url"
value="jdbc:mysql://localhost:3306/pullhand">
</property>
<property name="username" value="root"></property>
<property name="password" value="815241"></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>com/ssh/beans/po/Customer.hbm.xml</value></list>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<bean id="customerDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref local="customerDAO" />
</property>
<property name="transactionAttributes">
<props>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<bean id="customerDAO" class="com.ssh.beans.dao.CustomerDAO">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<bean name="/customer" class="com.ssh.struts.action.CustomerAction" >
<property name="customerDAO"><ref bean="customerDAOProxy"/></property>
</bean>
</beans>
package com.ssh.struts.action;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import com.ssh.beans.po.Customer;
public class CustomerAction extends Action {
ICustomerDAO customerDAO=null;
public void setCustomerDAO(ICustomerDAO customerDAO){
this.customerDAO=customerDAO;
}
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
List list=new ArrayList();
Customer customer=null;
if(customerDAO!=null){
list=customerDAO.getALLCustomer();
for(int i=0;i<list.size();i++){
customer=(Customer)list.get(i);
System.out.println("OK:"+customer.getCustName());
}
}else{
System.out.println("ERROR or NULL");
}
return mapping.findForward("success");
}
}HTTP Status 404 - Servlet action is not available
Apache Tomcat/5.0.28
<list>
<value>
com/ssh/beans/po/Customer.hbm.xml
</value>
</list>
</property>
本文包含以下内容Q?br />
·配置Hibernate和事?br />
·装蝲Spring的applicationContext.xml文g
·建立业务层和DAO之间的依赖关p?br />
·Spring应用到Struts?br />
?/strong>
q个例子是徏立一个简单的web应用Q叫MyUsers,完成用户理操作Q包含简单的数据库增Q删Q查Q该即CRUDQ新建,讉KQ更斎ͼ删除Q操作。这是一个三层的web应用Q通过ActionQStrutsQ访问业务层Q业务层讉KDAO。图一要说明了该应用的Ml构。图上的数字说明了流E顺序-从webQUserActionQ到中间层(UserManagerQ,再到数据讉K层(UserDAOQ,然后结果返回?br />
Spring层的真正强大在于它的声明型事务处理,帮定和对持久层支持(例如Hiberate和iBATISQ?br />
以下下是完成q个例子的步骤:
1Q?安装Eclipse插g
2Q?数据库徏?br />
3Q?配置Hibernate和Spring
4Q?建立Hibernate DAO接口的实现类
5Q?q行试c,试DAO的CRUD操作
6Q?创徏一个处理类Q声明事?br />
7Q?创徏web层的Action和model
8Q?q行Action的测试类试CRUD操作
9Q?创徏jsp文g通过览器进行CRUD操作
10Q?通过览器校验jsp
安装eclipse插g
1Q?Hibernate插ghttp://www.binamics.com/hibernatesync
2Q?Spring插ghttp://springframework.sourceforge.net/spring-ide/eclipse/updatesite/
3Q?MyEclipse插g(破解?
4Q?Tomcat插g. tanghan
5Q?其他插g包括xmlQjspQ?br />
数据库徏?/strong>
create table app_user(id number not null primary,firstname vchar(32),lastname vchar(32));
新徏目
新徏一个web projectQ新建后的目录结构同时包含了新徏文g夹page用于放jsp文gQ和源文件夹test用于放junit试文g。同时将用到的包Q包括strutsQhibernateQspring都导入到lib目录下?br />
创徏持久层O/R mapping
1Q?在src/com.jandar.model下用hibernate插g从数据库导出app_user?hbm.xml文g改名为User.hbm.xml
Q?xml version="1.0"?Q?br />
Q?DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" Q?br />
Qhibernate-mapping package="com.jandar.model"Q?br />
Qclass name="User" table="APP_USER"Q?br />
Qid
column="ID"
name="id"
type="integer"
Q?br />
Qgenerator class="assigned" /Q?br />
Q?idQ?br />
Qproperty
column="LASTNAME"
length="10"
name="lastname"
not-null="false"
type="string"
/Q?br />
Qproperty
column="FIRSTNAME"
length="10"
name="firstname"
not-null="true"
type="string"
/Q?br />
Q?classQ?br />
Q?hibernate-mappingQ?
2Q?通过hibernate synchronizer-Qsynchronizer file生成User.java文g,User对象对应于数据库中的app_user?br />
注:在eclipse下自动生成的对象文g不完全相同,相同的是每个对象文g必须实现Serializable接口Q必需又toString和hashCodeҎQ?br />
import java.io.Serializable;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
public class BaseObject implements Serializable {
public String toString() {
return ToStringBuilder.reflectionToString(this,
ToStringStyle.MULTI_LINE_STYLE);
}
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}
public class User extends BaseObject {
private Long id;
private String firstName;
private String lastName;
/**
* @return Returns the id.
*/
public Long getId() {
return id;
}
/**
* @param id The id to set.
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return Returns the firstName.
*/
public String getFirstName() {
return firstName;
}
/**
* @param firstName The firstName to set.
*/
public void setFirstName(String firstName) {
this.firstName = firstName;
}
/**
* @return Returns the lastName.
*/
public String getLastName() {
return lastName;
}
/**
* @param lastName The lastName to set.
*/
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
创徏DAO讉K对象
1Q?在src/com.jandar.service.dao新徏IDAO.java接口Q所有的DAO都承该接口
package com.jandar.services.dao;
public interface IDAO {
}
2Q?在src/com.jandar.service.dao下新建IUserDAO.java接口
public interface IUserDAO extends DAO {
List getUsers();
User getUser(Integer userid);
void saveUser(User user);
void removeUser(Integer id);
}
该接口提供了讉K对象的方法,
3Q?在src/com.jandar.service.dao.hibernate下新建UserDAOHiberante.java
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.hibernate.support.HibernateDaoSupport;
import com.jandar.model.User;
import com.jandar.service.dao.IUserDAO;
public class UserDaoHibernate extends HibernateDaoSupport implements IUserDAO {
private Log log=LogFactory.getLog(UserDaoHibernate.class);
/* Q非 JavadocQ?br />
* @see com.jandar.dao.IUserDAO#getUsers()
*/
public List getUsers() {
return getHibernateTemplate().find("from User");
}
/* Q非 JavadocQ?br />
* @see com.jandar.dao.IUserDAO#getUser(java.lang.Long)
*/
public User getUser(Integer id) {
// TODO 自动生成Ҏ存根
return (User) getHibernateTemplate().get(User.class,id);
}
/* Q非 JavadocQ?br />
* @see com.jandar.dao.IUserDAO#saveUser(com.jandar.model.User)
*/
public void saveUser(User user) {
log.debug("xxxxxxx");
System.out.println("yyyy");
getHibernateTemplate().saveOrUpdate(user);
if(log.isDebugEnabled())
{
log.debug("userId set to "+user.getId());
}
}
/* Q非 JavadocQ?br />
* @see com.jandar.dao.IUserDAO#removeUser(java.lang.Long)
*/
public void removeUser(Integer id) {
Object user=getHibernateTemplate().load(User.class,id);
getHibernateTemplate().delete(user);
if(log.isDebugEnabled()){
log.debug("del user "+id);
}
}
}
在这个类中实CIUserDAO接口的方法,q且l承了HibernateDAOSupportcR这个类的作用是通过hibernate讉K、操作对象,q而实现对数据库的操作?
]]>
文g的上传和下蝲在J2EE~程已经是一个非常古老的话题了,也许您马上就能掰着指头数出好几个著名的大gQ如SmartUpload、Apache的FileUpload。但如果您的目是构建在Struts+Spring+HibernateQ以下称SSHQ框架上的,q些大g显得笨重而桑了QSSH提供了一个简h便的文g上传下蝲的方案,我们只需要通过一些配|ƈ辅以量的代码就可以完好解决q个问题了?br />
本文围lSSH文g上传下蝲的主题,向您详细讲述如何开发基于SSH的WebE序。SSH各框架的均ؓ当前最新版本:
·Struts 1.2
·Spring 1.2.5
·Hibernate 3.0
本文选用的数据库为Oracle 9iQ当然你可以在不改动代码的情况下Q通过配置文g的调整将其移植到MhBlob字段cd的数据库上,如MySQLQSQLServer{?br />
M实现
上传文g保存到T_FILE表中QT_FILE表结构如下:
?1 T_FILE表结?/div>
其中Q?br />
·FILE_IDQ文件IDQ?2个字W,用Hibernate的uuid.hex法生成?br />
·FILE_NAMEQ文件名?br />
·FILE_CONTENTQ文件内容,对应Oracle的Blobcd?br />
·REMARKQ文件备注?br />
文g数据存储在Blobcd的FILE_CONTENT表字D上Q在Spring中采用OracleLobHandler来处理Lob字段Q包括Clob和BlobQ,׃在程序中不需要引用到oracle数据驱动E序的具体类且屏蔽了不同数据库处理Lob字段Ҏ上的差别Q从而撤除程序在多数据库UL上的樊篱?
1Q首先数据表中的Blob字段在Java领域对象中声明ؓbyte[]cdQ而非java.sql.Blobcd?br />
2Q数据表Blob字段在Hibernate持久化映文件中的type为org.springframework.orm.hibernate3.support.BlobByteArrayTypeQ即Spring所提供的用戯定义的类型,而非java.sql.Blob?
3Q在Spring中用org.springframework.jdbc.support.lob.OracleLobHandler处理Oracle数据库的Blobcd字段?br />
通过q样的设|和配置Q我们就可以象持久化表的一般字D늱型一样处理Blob字段了?br />
以上是SpringQHibernate文件二q制数据持久化到数据库的解决ҎQ而Struts通过表单中filecd的组件映ؓActionForm中类型ؓorg.apache.struts.upload. FormFile的属性来获取表单提交的文件数据?br />
lg所qͼ我们可以通过?2Q描l出SSH处理文g上传的方案:
?2 SSH处理文g上传技术方?/div>
文g上传的页面如?3所C:
?3 文g上传面
文g下蝲的页面如?4所C:
?4 文g下蝲面
该工E的资源l构如图 5所C:
?5 工程资源l构
工程的类按SSH的层ơ结构划分ؓ数据持久层、业务层和Web层;WEB-INF下的applicationContext.xml为Spring的配|文Ӟstruts-config.xml为Struts的配|文Ӟfile-upload.jsp为文件上传页面,file-list.jsp为文件列表页面?br />
本文后面的章节将从数据持久层-Q业务层-QWeb层的开发顺序,逐层讲解文g上传下蝲的开发过E?br />
数据持久?/strong>
1、领域对象及映射文g
您可以用Hibernate Middlegen、HIbernate Tools、Hibernate Syhchronizer{工h手工的方式,~写Hibernate的领域对象和映射文g。其中对应T_FILE表的领域对象Tfile.java为:
代码 1 领域对象Tfile
1. package sshfile.model;
2. public class Tfile
3.{
4. private String fileId;
5. private String fileName;
6. private byte[] fileContent;
7. private String remark;
8. …//getter and setter
9. }
特别需要注意的是:数据库表为Blobcd的字D在Tfile中的fileContentcd为byte[]。Tfile的Hibernate映射文gTfile.hbm.xml攑֜Tfile .javacL件的相同目录下:
代码 2 领域对象映射文g
1. Q?xml version="1.0"?Q?br />
2. Q?DOCTYPE hibernate-mapping PUBLIC
3. "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
4. "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" Q?br />
5. Qhibernate-mappingQ?br />
6. Qclass name="sshfile.model.Tfile" table="T_FILE"Q?br />
7. Qid name="fileId" type="java.lang.String" column="FILE_ID"Q?br />
8. Qgenerator class="uuid.hex"/Q?br />
9. Q?idQ?br />
10. Qproperty name="fileContent"
11. type="org.springframework.orm.hibernate3.support.BlobByteArrayType"
12. column="FILE_CONTENT" lazy="true"/Q?br />
13. …//其它一般字D늚映射
14. Q?classQ?br />
15. Q?hibernate-mappingQ?/td>
fileContent字段映射为Spring所提供的BlobByteArrayTypecdQBlobByteArrayType是用戯定义的数据类型,它实CHibernate 的org.hibernate.usertype.UserType接口。BlobByteArrayType使用从sessionFactory获取的Lob操作句柄lobHandlerbyte[]的数据保存到Blob数据库字D中。这P我们再没有必要通过编码的方式Q先insert然后再update来完成Blobcd数据的持久化Q这个原来难伺候的老爷l于被^民化了。关于lobHandler的配|请见本文后面的内容?br />
此外lazy="true"说明地返回整个Tfile对象Ӟq不q回fileContentq个字段的数据,只有在显式调用tfile.getFileContent()Ҏ时才真正从数据库中获取fileContent的数据。这是Hibernate3引入的新Ҏ,对于包含重量U大数据的表字段Q这U抽取方式提高了对大字段操作的灵zL,否则加蝲Tfile对象的结果集时如果Lq回fileContentQ这U批量的数据抽取可以引h据库?z泛效应"?br />
2、DAO~写和配|?br />
Spring面向接口~程Q所以我们将所有对Tfile的数据操作的Ҏ定义在TfileDAO接口中,q些接口Ҏ分别是:
·findByFildId(String fileId)
·save(Tfile tfile)
·List findAll()
TfileDAOHibernate提供了对TfileDAO接口ZHibernate的实玎ͼ如代?3所C:
代码 3 ZHibernate 的fileDAO实现c?br />
1. package sshfile.dao;
2.
3. import sshfile.model.*;
4. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
5. import java.util.List;
6.
7. public class TfileDAOHibernate
8. extends HibernateDaoSupport implements TfileDAO
9. {
10. public Tfile findByFildId(String fileId)
11. {
12. return (Tfile) getHibernateTemplate().get(Tfile.class, fileId);
13. }
14. public void save(Tfile tfile)
15. {
16. getHibernateTemplate().save(tfile);
17. getHibernateTemplate().flush();
18. }
19. public List findAll()
20. {
21. return getHibernateTemplate().loadAll(Tfile.class);
22. }
23. }
TfileDAOHibernate通过扩展Spring提供的Hibernate支持cHibernateDaoSupport而徏立,HibernateDaoSupport装了HibernateTemplateQ而HibernateTemplate装了Hibernate所提供几乎所有的的数据操作方法,如execute(HibernateCallback action)Qload(Class entityClass, Serializable id)Qsave(final Object entity){等?br />
所以我们的DAO只需要简单地调用父类的HibernateTemplate可以完成几乎所有的数据库操作了?br />
׃Spring通过代理Hibernate完成数据层的操作Q所以原Hibernate的配|文件hibernate.cfg.xml的信息也转移到Spring的配|文件中Q?br />
代码 4 Spring中有关Hibernate的配|信?br />
1. QbeansQ?br />
2. Q?-- 数据源的配置 //--Q?br />
3. Qbean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
4. destroy-method="close"Q?br />
5. Qproperty name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/Q?br />
6. Qproperty name="url" value="jdbc:oracle:thin:@localhost:1521:ora9i"/Q?br />
7. Qproperty name="username" value="test"/Q?br />
8. Qproperty name="password" value="test"/Q?br />
9. Q?beanQ?br />
10. Q?-- Hibernate会话工厂配置 //--Q?br />
11. Qbean id="sessionFactory"
12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"Q?br />
13. Qproperty name="dataSource" ref="dataSource"/Q?br />
14. Qproperty name="mappingDirectoryLocations"Q?br />
15. QlistQ?br />
16. QvalueQclasspath:/sshfile/modelQ?valueQ?br />
17. Q?listQ?br />
18. Q?propertyQ?br />
19. Qproperty name="hibernateProperties"Q?br />
20. QpropsQ?br />
21. Qprop key="hibernate.dialect"Qorg.hibernate.dialect.OracleDialectQ?propQ?br />
22. Qprop key="hibernate.cglib.use_reflection_optimizer"QtrueQ?propQ?br />
23. Q?propsQ?br />
24. Q?propertyQ?br />
25. Q?beanQ?br />
26. Q?-- Hibernate 模板//--Q?br />
27. Qbean id="hibernateTemplate"
28. class="org.springframework.orm.hibernate3.HibernateTemplate"Q?br />
29. Qproperty name="sessionFactory" ref="sessionFactory"/Q?br />
30. Q?beanQ?br />
31. Q?--DAO配置 //--Q?br />
32. Qbean id="tfileDAO" class="sshfile.dao.TfileDAOHibernate"Q?br />
33. Qproperty name="hibernateTemplate" ref="hibernateTemplate" /Q?br />
34. Q?beanQ?br />
35. …
36. Q?beansQ?/td>
W?~9行定义了一个数据源Q其实现cLapache的BasicDataSourceQ第11~25行定义了Hibernate的会话工厂,会话工厂cȝSpring提供的LocalSessionFactoryBeanl护Q它注入了数据源和资源映文Ӟ此外q通过一些键值对讄了Hibernate所需的属性?br />
其中W?6行通过c\径的映射方式Q将sshfile.modelcd目录下的所有领域对象的映射文g装蝲q来Q在本文的例子里Q它装载进Tfile.hbm.xml映射文g。如果有多个映射文g需要声明,使用c\径映方式显然比直接单独指定映射文g名的方式要简ѝ?
W?7~30行定义了Spring代理Hibernate数据操作的HibernateTemplate模板Q而第32~34行将该模板注入到tfileDAO中?br />
需要指定的是Spring 1.2.5提供了两套Hibernate的支持包Q其中Hibernate 2相关的封装类位于org.springframework.orm.hibernate2.*包中Q而Hibernate 3.0的封装类位于org.springframework.orm.hibernate3.*包中Q需要根据您所选用Hibernate版本q行正确选择?br />
3、Lob字段处理的配|?br />
我们前面已经指出Oracle的Lob字段和一般类型的字段在操作上有一个明昄区别--那就是你必须首先通过Oracle的empty_blob()/empty_clob()初始化Lob字段Q然后获取该字段的引用,通过q个引用更改其倹{所以要完成对Lob字段的操作,Hibernate必须执行两步数据库访问操作,先Insert再Update?br />
使用BlobByteArrayType字段cd后,Z么我们就可以象一般的字段cd一h作Blob字段呢?可以定的一ҎQBlobByteArrayType不可能逾越Blob天生的操作方式,原来是BlobByteArrayType数据cd本n具体数据讉K的功能,它通过LobHandler两ơ数据访问的动作隐藏hQBlob字段的操作在表现上和其他一般字D业cd无异Q所以LobHandlerx那个"苦了我一个,q福十亿?的那位幕后英雄?br />
LobHandler必须注入到Hibernate会话工厂sessionFactory中,因ؓsessionFactory负责产生与数据库交互的Session。LobHandler的配|如代码 5所C:
代码 5 Lob字段的处理句柄配|?br />
1. QbeansQ?br />
2. …
3. Qbean id="nativeJdbcExtractor"
4. class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"
5. lazy-init="true"/Q?br />
6. Qbean id="lobHandler"
7. class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true"Q?br />
8. Qproperty name="nativeJdbcExtractor"Q?br />
9. Qref local="nativeJdbcExtractor"/Q?br />
10. Q?propertyQ?br />
11. Q?beanQ?br />
12. …
13. Q?beansQ?/td>
首先Q必d义一个能够从q接池中抽取出本地数据库JDBC对象Q如OracleConnectionQOracleResultSet{)的抽取器QnativeJdbcExtractorQ这h可以执行一些特定数据库的操作。对于那些仅装了Connection而未包括Statement的简单数据连接池QSimpleNativeJdbcExtractor是效率最高的抽取器实现类Q但具体到apache的BasicDataSourceq接池,它封装了所有JDBC的对象,q时需要用CommonsDbcpNativeJdbcExtractor了。Spring针对几个著名的Web服务器的数据源提供了相应的JDBC抽取器:
·WebLogicQWebLogicNativeJdbcExtractor
·WebSphereQWebSphereNativeJdbcExtractor
·JBossQJBossNativeJdbcExtractor
在定义了JDBC抽取器后Q再定义lobHandler。Spring 1.2.5提供了两个lobHandlerQ?br />
·DefaultLobHandlerQ适用于大部分的数据库Q如SqlServerQMySQLQ对Oracle 10g也适用Q但不适用于Oracle 9iQ看来Oracle 9i实是个怪胎Q谁叫Oracle 公司自己都说Oracle 9i是一个过渡性的产品呢)?br />
·OracleLobHandlerQ适用于Oracle 9i和Oracle 10g?br />
׃我们的数据库是Oracle9iQ所以用OracleLobHandler?br />
在配|完LobHandler后, q需要将其注入到sessionFactory的Bean中,下面是调用后的sessionFactory Bean的配|:
代码 6 lobHandler注入到sessionFactory中的配置
1. QbeansQ?br />
2. …
3. Qbean id="sessionFactory"
4. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"Q?br />
5. Qproperty name="dataSource" ref="dataSource"/Q?br />
6. Q?-- 为处理Blobcd字段的句柄声?//--Q?br />
7. Qproperty name="lobHandler" ref="lobHandler"/Q?br />
8. …
9. Q?beanQ?br />
10. …
11. Q?beansQ?/td>
如第7所C,通过sessionFactory的lobHandler属性进行注入?br />
业务?/strong>
1、业务层接口
"面向接口而非面向cȝE?是Spring不遗余力所推荐的编E原则,q条原则也已lؓ大部开发者所接受Q此外,JDK的动态代理只Ҏ口有效,否则必须使用CGLIB生成目标cȝ子类。我们依从于Spring的倡导Z务类定义一个接口:
代码 7 业务层操作接?br />
1. public interface FileService
2. {
3. void save(FileActionForm fileForm);//提交的上传文g保存到数据表?br />
4. List getAllFile();//得到T_FILE所C?br />
5. void write(OutputStream os,String fileId);//某个文件的文g数据写出到输出流?br />
6. String getFileName(String fileId);//获取文g?br />
7. }
其中save(FileActionForm fileForm)ҎQ将装在fileForm中的上传文g保存到数据库中,q里我们使用FileActionForm作ؓҎ入参QFileActionForm是Web层的表单数据对象Q它装了提交表单的数据。将FileActionForm直接作ؓ业务层的接口入参Q相当于Web层传播到业务层中去,卛_业务层绑定在特定的Web层实现技术中Q按照分层模型学院派的观点,q是一U反模块化的设计Q但?一?的业务系lƈ无需提供多种UI界面Q系lWeb层将来切换到另一U实现技术的可能性也微乎其微Q所以笔者觉得没有必要ؓ了这个业务层完全独立于调用层的过高目标而去搞一个额外的隔离层,费了原材料不说Q还系l搞得过于复杂,相比于其它原则,"?始终是最大的一条原则?br />
getAllFile()负责获取T_FILE表所有记录,以便在网上昄出来?br />
而getFileName(String fileId)和write(OutputStream os,String fileId)则用于下载某个特定的文g。具体的调用是将Web层将response.getOutputStream()传给write(OutputStream os,String fileId)接口Q业务层直接文件数据输出到q个响应中。具体实现请参见错误Q未扑ֈ引用源。节下蝲文g部分?br />
2、业务层接口实现c?br />
FileService的实现类为FileServiceImplQ其中save(FileActionForm fileForm)的实现如下所C:
代码 8 业务接口实现cMsave()
1. …
2. public class FileServiceImpl
3. implements FileService
4. {
5. private TfileDAO tfileDAO;
6. public void save(FileActionForm fileForm)
7. {
8. Tfile tfile = new Tfile();
9. try
10. {
11. tfile.setFileContent(fileForm.getFileContent().getFileData());
12. }
13. catch (FileNotFoundException ex)
14. {
15. throw new RuntimeException(ex);
16. }
17. catch (IOException ex)
18. {
19. throw new RuntimeException(ex);
20. }
21. tfile.setFileName(fileForm.getFileContent().getFileName());
22. tfile.setRemark(fileForm.getRemark());
23. tfileDAO.save(tfile);
24. }
25. …
26. }
在save(FileActionForm fileForm)Ҏ里,完成两个步骤Q?br />
其一Q象在水桉倒水一PFileActionForm对象中的数据倒入到Tfile对象中;
其二Q调用TfileDAO保存数据?br />
需要特别注意的是代码的W?1行,FileActionForm的fileContent属性ؓorg.apache.struts.upload.FormFilecdQFormFile提供了一个方便的ҎgetFileData()Q即可获取文件的二进制数据。通过解读FormFile接口实现cDiskFile的原码,我们可能知道FormFile本nq不~存文g的数据,只有实际调用getFileData()Ӟ才从盘文g输入中获取数据。由于FormFile使用读取方式获取数据,本n没有~存文g的所有数据,所以对于上传超大体U的文gQ也是没有问题的Q但是,׃数据持久层的Tfile使用byte[]来缓存文件的数据Q所以ƈ不适合处理大体积的文Ӟ?00MQ,对于大体积的文Ӟ依然需要用java.sql.Blobcd以常规流操作的方式来处理?br />
此外Q通过FileForm的getFileName()Ҏ可以获得上传文件的文g名,如第21行代码所C?br />
write(OutputStream os,String fileId)Ҏ的实玎ͼ如代?9所C:
代码 9 业务接口实现cMwrite()
1. …
2. public class FileServiceImpl
3. implements FileService
4. {
5.
6. public void write(OutputStream os, String fileId)
7. {
8. Tfile tfile = tfileDAO.findByFildId(fileId);
9. try
10. {
11. os.write(tfile.getFileContent());
12. os.flush();
13. }
14. catch (IOException ex)
15. {
16. throw new RuntimeException(ex);
17. }
18. }
19. …
20. }
write(OutputStream os,String fileId)也简单地分ؓ两个操作步骤Q首先,ҎfileId加蝲表记录,然后fileContent写入到输出流中?br />
3、Spring事务配置
下面Q我们来看如何在Spring配置文g中ؓFileService配置声明性的事务
1. QbeansQ?br />
2. …
3. Qbean id="transactionManager"
4. class="org.springframework.orm.hibernate3.HibernateTransactionManager"Q?br />
5. Qproperty name="sessionFactory" ref="sessionFactory"/Q?br />
6. Q?beanQ?br />
7. Q?-- 事务处理的AOP配置 //--Q?br />
8. Qbean id="txProxyTemplate" abstract="true"
9. class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"Q?br />
10. Qproperty name="transactionManager" ref="transactionManager"/Q?br />
11. Qproperty name="transactionAttributes"Q?br />
12. QpropsQ?br />
13. Qprop key="get*"QPROPAGATION_REQUIRED,readOnlyQ?propQ?br />
14. Qprop key="find*"QPROPAGATION_REQUIRED,readOnlyQ?propQ?br />
15. Qprop key="save"QPROPAGATION_REQUIREDQ?propQ?br />
16. Qprop key="write"QPROPAGATION_REQUIRED,readOnlyQ?propQ?br />
17. Q?propsQ?br />
18. Q?propertyQ?br />
19. Q?beanQ?br />
20. Qbean id="fileService" parent="txProxyTemplate"Q?br />
21. Qproperty name="target"Q?br />
22. Qbean class="sshfile.service.FileServiceImpl"Q?br />
23. Qproperty name="tfileDAO" ref="tfileDAO"/Q?br />
24. Q?beanQ?br />
25. Q?propertyQ?br />
26. Q?beanQ?br />
27. Q?beansQ?/td>
Spring的事务配|包括两个部分:
其一Q定义事务管理器transactionManagerQ用HibernateTransactionManager实现事务理Q?br />
其二Q对各个业务接口q行定义Q其实txProxyTemplate和fileService是父子节点的关系Q本来可以将txProxyTemplate定义的内容合q到fileService中一起定义,׃我们的系l仅有一个业务接口需要定义,所以将其定义的一部分抽象到父节点txProxyTemplate中意义确实不大,但是对于真实的系l,往往拥有为数众多的业务接口需要定义,这些业务接口定义内容的共同部分抽取C个父节点中,然后在子节点中通过parentq行兌Q就可以大大化业务接口的配置了?br />
父节点txProxyTemplate注入了事务管理器Q此外还定义了业务接口事务管理的ҎQ允讔R过通配W的方式q行匚w声明Q如前两个接口方法)Q有些接口方法仅Ҏ据进行读操作Q而另一些接口方法需要涉及到数据的更攏V对于前者,可以通过readOnly标识出来Q这h利于操作性能的提高,需要注意的是由于父c节点定义的Bean仅是子节炚w|信息的抽象Qƈ不能具体实现化一个Bean对象Q所以需要特别标注ؓabstract="true"Q如W?行所C?br />
fileService作ؓ一个目标类被注入到事务代理器中Q而fileService实现cL需要的tfileDAO实例Q通过引用3.2节中定义的tfileDAO Bean注入?br />
Web层实?/strong>
1、Web层的构g和交互流E?br />
Web层包括主?个功能:
·上传文g?br />
·列出所有已l上传的文g列表Q以供点M载?br />
·下蝲文g?br />
Web层实现构件包括与2个JSP面Q?个ActionForm及一个ActionQ?br />
·file-upload.jspQ上传文件的面?br />
·file-list.jspQ已l上传文件的列表面?br />
·FileActionFormQfile-upload.jsp面表单对应的ActionForm?br />
·FileActionQ承org.apache.struts.actions.DispatchAction的ActionQ这栯个Action可以通过一个URL参数区分中响应不同的h?br />
Web层的q些构g的交互流E如?6所C:
?6 Web层Struts程?/div>
其中Q在执行文g上传的请求时QFileAction在执行文件上传后Qforward到loadAllFile出口中,loadAllFile加蝲数据库中所有已l上传的记录Q然后forward到名为fileListPage的出口中Q调用file-list.jsp面昄已经上传的记录?br />
2、FileAction功能
Struts 1.0的Action有一个弱:一个Action只能处理一U请求,Struts 1.1中引入了一个DispatchActionQ允讔R过URL参数指定调用Action中的某个ҎQ如http://yourwebsite/fileAction.do?method=upload卌用FileAction中的uploadҎ。通过q种方式Q我们就可以一些相关的h集中C个Action当中~写Q而没有必要ؓ某个h操作~写一个ActioncR但是参数名是要在struts-config.xml中配|的Q?br />
1. Qstruts-configQ?br />
2. Qform-beansQ?br />
3. Qform-bean name="fileActionForm" type="sshfile.web.FileActionForm" /Q?br />
4. Q?form-beansQ?br />
5. Qaction-mappingsQ?br />
6. Qaction name="fileActionForm" parameter="method" path="/fileAction"
7. type="sshfile.web.FileAction"Q?br />
8. Qforward name="fileListPage" path="/file-list.jsp" /Q?br />
9. Qforward name="loadAllFile" path="/fileAction.do?method=listAllFile" /Q?br />
10. Q?actionQ?br />
11. Q?action-mappingsQ?br />
12. Q?struts-configQ?/td>
W?行的parameter="method"指定了承载方法名的参敎ͼW?行中Q我们还配置了一个调用FileAction不同Ҏ的Action出口?br />
FileAction共有3个请求响应的ҎQ它们分别是Q?br />
·upload(…)Q处理上传文件的h?br />
·listAllFile(…)Q处理加载数据库表中所有记录的h?br />
·downloadQ?#8230;Q:处理下蝲文g的请求?br />
下面我们分别对这3个请求处理方法进行讲解?br />
2.1 上传文g
上传文g的请求处理方法非常简单,之言之,是从Spring容器中获取业务层处理cFileServiceQ调用其save(FileActionForm form)Ҏ上传文gQ如下所C:
1. public class FileAction
2. extends DispatchAction
3. {
4. //上传文件保存到数据库中
5. public ActionForward upload(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. {
9. FileActionForm fileForm = (FileActionForm) form;
10. FileService fileService = getFileService();
11. fileService.save(fileForm);
12. return mapping.findForward("loadAllFile");
13. }
14. //从Spring容器中获取FileService对象
15. private FileService getFileService()
16. {
17. ApplicationContext appContext = WebApplicationContextUtils.
18. getWebApplicationContext(this.getServlet().getServletContext());
19. return (FileService) appContext.getBean("fileService");
20. }
21. …
22. }
׃FileAction其它两个h处理Ҏ也需要从Spring容器中获取FileService实例Q所以我们特别提供了一个getFileService()ҎQ第15~21行)。重构的一条原则就是:"发现代码中有重复的表辑ּQ将其提取ؓ一个变量;发现cM有重复的代码D,其提取Z个方法;发现不同cM有相同的ҎQ将其提取ؓ一个类"。在真实的系l中Q往往拥有多个Action和多个Servicec,q时一个比较好的设|思\是,提供一个获取所有Service实现对象的工LQ这样就可以Spring 的Service配置信息屏蔽在一个类中,否则Service的配|名字散落在E序各处Q维护性是很差的?br />
2.2 列出所有已l上传的文g
listAllFileҎ调用Servie层方法加载T_FILE表中所有记录,q将其保存在Request域中Q然后forward到列表页面中Q?br />
1. public class FileAction
2. extends DispatchAction
3. {
4. …
5. public ActionForward listAllFile(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. throws ModuleException
9. {
10. FileService fileService = getFileService();
11. List fileList = fileService.getAllFile();
12. request.setAttribute("fileList",fileList);
13. return mapping.findForward("fileListPage");
14. }
15. }
file-list.jsp面使用Struts标签展示Z存在Request域中的记录:
1. Q?@page contentType="text/html; charset=GBK"%Q?br />
2. Q?@taglib uri="/WEB-INF/struts-logic.tld" prefix="logic"%Q?br />
3. Q?@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%Q?br />
4. QhtmlQ?br />
5. QheadQ?br />
6. QtitleQfile-downloadQ?titleQ?br />
7. Q?headQ?br />
8. Qbody bgcolor="#ffffff"Q?br />
9. QolQ?br />
10. Qlogic:iterate id="item" name="fileList" scope="request"Q?br />
11. QliQ?br />
12. Qa href='fileAction.do?method=download&fileId=
13. Qbean:write name="item"property="fileId"/Q?Q?br />
14. Qbean:write name="item" property="fileName"/Q?br />
15. Q?aQ?br />
16. Q?liQ?br />
17. Q?logic:iterateQ?br />
18. Q?olQ?br />
19. Q?bodyQ?br />
20. Q?htmlQ?/td>
展现面的每条记录挂接着一个链接地址QŞ如:fileAction.do?method=download&fileId=xxxQmethod参数指定了这个请求由FileAction的downloadҎ来响应,fileId指定了记录的主键?br />
׃在FileActionForm中,我们定义了fileId的属性,所以在download响应Ҏ中,我们可以从FileActionForm中取得fileId的倹{这里涉及到一个处理多个请求Action所对应的ActionForm的设计问题,׃原来的Action只能对应一个请求,那么原来的ActionForm非常单,它仅需要将q个h的参数项作ؓ其属性就可以了,但现在一个Action对应多个hQ每个请求所对应的参数项是不一LQ此时的ActionForm的属性就必须是多h参数的q了。所以,除了文g上传h所对应的fileContent和remark属性外q包括文件下载的fileId属性:
?7 FileActionForm
当然q样会造成属性的冗余Q比如在文g上传的请求中Q只会用到fileContent和remark属性,而在文g下蝲的请求时Q只会用到fileId属性。但q种冗余是会带来好处?-它得一个Action可以处理多个h?br />
2.3 下蝲文g
在列表页面中点击一个文件下载,其请求由FileAction的downloadҎ来响应,downloadҎ调用业务层的FileServiceҎQ获取文件数据ƈ写出到response的响应流中。通过合理讄HTTP响应头参敎ͼ响应流在客L表现Z个下载文件对话框Q其代码如下所C:
代码 10 业务接口实现cMdownload
1. public class FileAction
2. extends DispatchAction
3. {
4. …
5. public ActionForward download(ActionMapping mapping, ActionForm form,
6. HttpServletRequest request,
7. HttpServletResponse response)
8. throws ModuleException
9. {
10. FileActionForm fileForm = (FileActionForm) form;
11. FileService fileService = getFileService();
12. String fileName = fileService.getFileName(fileForm.getFileId());
13. try
14. {
15. response.setContentType("application/x-msdownload");
16. response.setHeader("Content-Disposition",
17. "attachment;" + " filename="+
18. new String(fileName.getBytes(), "ISO-8859-1"));
19. fileService.write(response.getOutputStream(), fileForm.getFileId());
20. }
21. catch (Exception e)
22. {
23. throw new ModuleException(e.getMessage());
24. }
25. return null;
26. }
27. }
W?5~18行,讄HTTP响应_响应类型设|ؓapplication/x-msdownload MIMEcdQ则响应在IE中将弹出一个文件下载的对话框,如图 4所C。IE所支持的MIMEcd多达26U,您可以通过q个|址查看其他的MIMEcdQ?br />
http://msdn.microsoft.com/workshop/networking/moniker/overview/appendix_a.asp?br />
如果下蝲文g的文件名含有中文字符Q如果不对其q行编码,如第18行所C,客户文g下蝲对话框中出现的文件名会发生q?br />
W?9行代码获得response的输出流Q作为FileServie write(OutputStream os,String fileId)的入参,q样文g的内容将写到response的输出流中?br />
3、web.xml文g的配|?br />
Spring容器在何时启动呢Q我可以在Web容器初始化来执行启动Spring容器的操作,Spring提供了两U方式启动的ҎQ?br />
·通过org.springframework.web.context .ContextLoaderListener容器监听器,在Web容器初始化时触发初始化Spring容器Q在web.xml中通过QlistenerQ</listenerQ对其进行配|?br />
·通过Servlet org.springframework.web.context.ContextLoaderServletQ将光|ؓ自动启动的ServletQ在Web容器初始化时Q通过q个Servlet启动Spring容器?br />
在初始化Spring容器之前Q必d初始化log4J的引擎,Spring也提供了容器监听器和自动启动Servlet两种方式对log4J引擎q行初始化:
·org.springframework.web.util .Log4jConfigListener
·org.springframework.web.util.Log4jConfigServlet
下面我们来说明如何配|web.xml启动Spring容器Q?br />
代码 11 web.xml中对应Spring的配|内?br />
1. Qweb-appQ?br />
2. Qcontext-paramQ?br />
3. Qparam-nameQcontextConfigLocationQ?param-nameQ?br />
4. Qparam-valueQ?WEB-INF/applicationContext.xmlQ?param-valueQ?br />
5. Q?context-paramQ?br />
6. Qcontext-paramQ?br />
7. Qparam-nameQlog4jConfigLocationQ?param-nameQ?br />
8. Qparam-valueQ?WEB-INF/log4j.propertiesQ?param-valueQ?br />
9. Q?context-paramQ?br />
10. QservletQ?br />
11. Qservlet-nameQlog4jInitServletQ?servlet-nameQ?br />
12. Qservlet-classQorg.springframework.web.util.Log4jConfigServletQ?servlet-classQ?br />
13. Qload-on-startupQ?Q?load-on-startupQ?br />
14. Q?servletQ?br />
15. QservletQ?br />
16. Qservlet-nameQspringInitServletQ?servlet-nameQ?br />
17. Qservlet-classQorg.springframework.web.context.ContextLoaderServletQ?servlet-classQ?br />
18. Qload-on-startupQ?Q?load-on-startupQ?br />
19. Q?servletQ?br />
20. …
21. Q?web-appQ?/td>
启动Spring容器Ӟ需要得C个信息:Spring配置文g的地址和Log4J属性文Ӟq两上信息分别通过contextConfigLocationWeb和log4jConfigLocation容器参数指定Q如果有多个Spring配置文gQ则用逗号隔开Q如Q?br />
/WEB-INF/applicationContext_1.xml, /WEB-INF/applicationContext_1.xm2
׃在启动ContextLoaderServlet之前Q必M先初始化Log4J的引擎,所以Log4jConfigServlet必须在ContextLoaderServlet之前启动Q这通过Qload-on-startupQ来指定它们启动的先后顺序?br />
q是开发Web应用E序一个比较老套又常见问题,׃不同Web应用服务器的默认~码是不一LQؓ了方便Web应用在不同的Web应用服务器上ULQ最好的做法是WebE序自n来处理编码{换的工作。经典的作法是在web.xml中配|一个编码{换过滤器QSpring提供了一个编码过滤器cCharacterEncodingFilterQ下面,我们为应用配|上q个qo器:
1. Qweb-appQ?br />
2. …
3. QfilterQ?br />
4. Qfilter-nameQencodingFilterQ?filter-nameQ?br />
5. Qfilter-classQorg.springframework.web.filter.CharacterEncodingFilterQ?filter-classQ?br />
6. Qinit-paramQ?br />
7. Qparam-nameQencodingQ?param-nameQ?br />
8. Qparam-valueQGBKQ?param-valueQ?br />
9. Q?init-paramQ?br />
10. Q?filterQ?br />
11. Qfilter-mappingQ?br />
12. Qfilter-nameQencodingFilterQ?filter-nameQ?br />
13. Qurl-patternQ?*Q?url-patternQ?br />
14. Q?filter-mappingQ?br />
15. …
16. Q?web-appQ?/td>
Spring的过滤器cLorg.springframework.web.filter.CharacterEncodingFilterQ通过encoding参数指定~码转换cd为GBKQ<filter-mappingQ的配置使该qo器截h有的L?br />
Struts的框架也需要在web.xml中配|,惛_读者朋友对Struts的配|都很熟悉,故在此不再提及,请参见本文所提供的源码?br />
ȝ
本文通过一个文件上传下载的Web应用Q讲解了如何构徏ZSSH的Web应用Q通过Struts和FormFileQSpring的LobHandler以及Spring为HibernateBlob处理所提供的用LBlobByteArrayType Q实C传和下蝲文g的功能仅需要廖廖数行的代码卛_完成。读者只需对程序作E许的调_卛_处理Clob字段Q?br />
·领域对象对应Clob字段的属性声明ؓStringcdQ?br />
·映射文g对应Clob字段的属性声明ؓorg.springframework.orm.hibernate3.support.ClobStringTypecd?br />
本文通过SSHҎ件上传下载简捷完的实现得以中Hv了解SSH强强联合构徏Web应用的强大优ѝ在行文中,q穿插了一些分层的设计l验Q配|技巧和Spring所提供的方便类Q相信这些知识对您的开发都有所裨益
]]>
վ֩ģ壺
ҹƷƬѹۿ
|
߹ۿվ|
Ʒ߹ۿ|
þþƷѿ|
þþþþaŷAV|
һƬaѲſ|
av乾|
þþƷѴƬƬ|
ҹþþþþ|
Ƶ߹ۿѲ|
һĻ|
ˬˬƵ߹ۿ|
þѾƷav|
Ʒ˳Ƶapp|
77777ҹ|
|
ҹƵ
|
պۺϾþþ|
ҹƵ|
avպۺһ߹ۿ|
AVƬ߹ۿ|
avӰ|
պƵѿ|
һëƬaaaaaaƵѿ|
AVþþƷ|
1024ƷƵר|
ŷ͵|
AV|
ĻMVƵ3|
337pձŷ|
Ƶ߹ۿѿƬ|
ҹ|
˳ɫ77777|
ŮAëƬ|
AVպAVվ|
߹ۿ|
òƵѸ|
ƷƵѹۿ|
ŷۺ|
˳վ߸|
18վѹۿ|