Struts1SH整合

来源:互联网 发布:淘宝兼职工资日结 编辑:程序博客网 时间:2024/06/05 07:53

Spring2.5+Hibernate3.5+Struts1.3整合开发

为了避免出现jar包不兼容,或者重复加载的情况,先把jar包整理出来:

hibernate核心安装包下的:
            hibernate3.jar
            lib/required/*
            lib/optional/ehcache-1.2.3.jar
hibernate 注解安装包下的:
            lib/test/slf4j-log4j12.jar
Spring安装包下的
            dist/spring.jar
            dist/modules/spring-webmvc-struts.jar
            lib/jakarta-commons/commons-logging.jar、commons-dbcp.jar、commons-pool.jar
            lib/aspectj/aspectjweaver.jar、aspectjrt.jar
            lib/cglib/cglib-nodep-2.1_3.jar
            lib/j2ee/common-annotations.jar
            lib/log4j/log4j-1.2.15.jar
Struts
            struts-1.3.8-lib.zip,需要使用到解压目录下的所有jar包,建议把jstl-1.0.2.jar和standard-1.0.2.jar更换为1.1版本。Spring中已经存在一个antlr-2.7.6.jar,所以把
            struts中的antlr-2.7.2.jar删除,避免jar冲突.
数据库驱动

替换旧的jar包,删除重复的jar包,jar包就准备好了,在此选用Mysql作为数据库,在临时数据库中创建一个person表,表的结构如下:

use test; create table person (        name varchar(12),               id   int )  

 


不要一开始就进行三大框架的整合,我们首先进行Spring与Hibernate的整合,然后再进行Spring与Struts的整合.

把要做的事情整理好,能够便于我们开发:

搭建Hibernate

  • 创建表
  • 创建对应的javaBean---Person类,放置在domain包下
  • 创建PersonDao,提供一个save方法,接收一个Person对象,放置在dao下
  • 创建BusinessService,维护一个PersonDao对象,对外界提供save功能,放置在service下
  • 配置person.hbm.xml文件,放置在domain包下
  • 配置hibernate.cfg.xml,放置在类路径下

表和实体创建好后,就开始配置person.hbm.xml文件:

<hibernate-mapping>     <!--          name:javaBean         table:对应的表名      -->     <class name="..domain.Person" table="person">         <!-- 配置主键 -->         <id name="id" type="integer">                             <column name="id"></column>             <!-- 自增 -->             <generator class="increment"></generator>         </id>         <!-- 配置其他列 -->         <property name="name" type="string">             <column name="name" sql-type="varchar(12)"></column>         </property>             </class> </hibernate-mapping>  

接下来配置hibernate.cfg.xml文件,相信大家对session-factory的配置很熟悉,就不再赘述了:

<hibernate-configuration>     <session-factory>         <property name="hibernate.connection.username">root</property>         <property name="hibernate.connection.password">root</property>         <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>         <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>         <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>         <property name="hibernate.show_sql">true</property>         <property name="hibernate.hbm2ddl.auto">update</property>         <mapping resource="../domain/Person.hbm.xml"/>     </session-factory> </hibernate-configuration> 

为了简化对数据库的操作,我们在PersonDao里面维护一个HibernateTemplate,标注注解,用Spring注入进来:

@Resourceprivate HibernateTemplate hibernateTemplate;

同样,BusinessService维护的PersonDao也使用注解注入:

@Resourceprivate PersonDao personDao;

Hibernate的搭建就完成了.


由于目前为止没有使用到Struts,我们不用在WEB上对Spring+Hibernate进行测试,用junit或者main方法都可以,在此用main方法测试.

搭建Spring

  • 创建RegisterAction类(为后续的Struts作准备),在类加入main方法,放入web.action包中
  • 配置beans.xml文件,放在类路径下

我们将在后面使用Spring的事务管理,所以要引入tx命名空间,事务管理器还需要被管理的切入点,不然就只是没有灵魂的空壳而已,aop命名空间也是必须的,还有对标注@Resource注解的字段进行注入,因而要引入context命名空间.

由于数据源在hibernate.cfg.xml文件中配置,hibernate中的session,就相当于Spring中的datasource,我们需要Spring获取hibernate.cfg.xml文件中的数据,Spring的org.springframework.orm.hibernate3包中,有着一系列整合hibernate框架的类,我们只需要使用LocalSessionFactoryBean类即可:

<!-- 这是Spring整合Hibernate的入口 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">     <!--          Spring生成sessionFactory需要Hibernate的配置文件Hibernate.cfg.xml          classpath:hibernate.cfg.xml: 表示在类路径下查找hibernate.cfg.xml文件,语法格式classpath:配置文件     -->             <property name="configLocation" value="classpath:hibernate.cfg.xml" /> </bean>  

Spring标榜自己的声明式事务管理是多么多么的强大,我们不对事务管理器进行配置,岂不是白费了Spring的一番苦心?

<!-- Spring支持5种事务管理器,分别对应不同的委托机制,我们现在使用Hibernate,就要使用Hibernate的事务管理器实现.         Spring支持的5种事务管理器:             org.springframework.jdbc.datasource.DataSourceTransactionManager             org.springframework.orm.hibernate3.HibernateTransactionManager             org.springframework.jdo.JdoTransactionManager             org.springframework.transaction.jta.JtaTransactionManager             org.springframework.orm.ojb.PersistenceBrokerTransactionManager -->              <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">     <property name="sessionFactory" ref="sessionFactory"></property> </bean>  

接下来该配置事务管理器的通知(其实事务管理器就是一个切面):

<!-- 配置通知      transaction-manager: 表示通知织入到的切面 --> <tx:advice id="advice" transaction-manager="txManager">     <tx:attributes>         <tx:method name="save*" read-only="false" isolation="DEFAULT" propagation="REQUIRED"/>         <!-- 如果是其它方法 -->         <tx:method name="*" read-only="true"/>     </tx:attributes> </tx:advice> 

切入点:

<!-- 配置切入点 --> <aop:config>     <aop:pointcut id="perform" expression="execution(* ..service..*.*(..))"/>  <!-- 本文所有包名都用..代替,此表达式包名有误 -->     <aop:advisor pointcut-ref="perform" advice-ref="advice"/> </aop:config> 

由于PersonDao中维护了一个HibernateTemplate,BusinessService中维护了一个PersonDao,我们需要在配置文件中对它们进行注入:

<!-- HibernateTemplate --> <bean id="hibernateTemplat" class="org.springframework.orm.hibernate3.HibernateTemplate">     <property name="sessionFactory" ref="sessionFactory"></property> </bean>  <!-- Dao层对象 --> <bean id="personDao" class=".t.dao.PersonDao"></bean>  <!-- 配置业务层对象 --> <bean id="service" class="..service.BusinessService"></bean>      <!-- 注册注解解析器 --> <context:annotation-config></context:annotation-config> 

最后再在RegisterAction中进行测试,RegisterAction:

public class RegisterAction {    //由于只是测试,不需要继承Action     public static void main(String[] args) throws Exception {         ApplicationContext act = new ClassPathXmlApplicationContext("beans.xml");         BusinessService service = (BusinessService) act.getBean("service");         Person person = new Person();         //id设置了自增,不用管         person.setName("zhang");         service.save(person);     } } 

注:由于PersonDao和BusinessService都没有实现接口,故代理对象使用cglib库生成目标对象的子类.如果对事务管理器工作产生怀疑,可以在dao中分别抛出运行时异常和编译时异常进行测试.


搭建Struts

  • 创建register.jsp和success.jsp文件,直接放在根目录下
  • RegisterAction继承自DispatchAction
  • 维护一个BusinessService,由Spring运行时注入
  • 配置struts-config.xml
  • 配置web.xml

register.jsp文件中只需要简单的一个表单即可,一个文本框,一个提交按钮,用不用Stusts的标签库都行,不过,我为了方便,在表单里面增加了一个隐藏域:

<input type="hidden" name="method" value="save" />

待会在Struts的配置文件中对方法进行转发就行了.

success.jsp文件中也随便输出什么就行了,只要是看到就代表Action的跳转成功了.

把RegisterAction稍微修改一下:

public class RegisterAction extends DispatchAction{    /**     * 加了点形参,接收Struts传递的对象     */    public ActionForward save(ActionMapping mapping, ActionForm form,            HttpServletRequest request, HttpServletResponse response) {                ApplicationContext act = new ClassPathXmlApplicationContext("beans.xml");        BusinessService service = (BusinessService) act.getBean("service");        Person person = new Person();        person.setName(request.getParameter("name"));        service.save(person);        return mapping.findForward("success");    }}

在WEB-INF下配置Struts的Action映射信息:

<struts-config>    <action-mappings>        <action path="/register"                 type="..web.action.RegisterAction"                 scope="request"                parameter="method">   //跟register.jsp中隐藏域的值对应            <forward name="success" path="/success.jsp"></forward>                </action>    </action-mappings></struts-config>

再将ActionServlet在web.xml文件中映射进来就行了.

测试,如果跳转到success.jsp页面就代表成功了.

 


好了,程序逻辑就是这样,但是有三点需要注意,首先,Hibernate的配置文件中配置了一个连接池,这是Hibernate的开发者为了让人们学习Hibernate而开发的一个简易连接池,这个连接池在实际应用中是有BUG的,不能使用.那么我们就要在Spring里面配置连接池,并且要交由Spring管理:

Hibernate配置文件修改后如下:

<hibernate-configuration>    <session-factory>        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>        <property name="hibernate.show_sql">true</property>        <property name="hibernate.hbm2ddl.auto">update</property>        <mapping resource="../../domain/Person.hbm.xml"/>    </session-factory></hibernate-configuration>

删除了关于连接的信息,然后在Spring的配置文件中加上连接池,以c3p0为例:

<!-- 配置数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">        <property name="driverClass" value="com.mysql.jdbc.Driver" />        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=GBK"/>        <property name="user" value="root" />        <property name="password" value="root" />        <!--连接池中保留的最小连接数。-->        <property name="minPoolSize" value="5" />            <!--连接池中保留的最大连接数。Default: 15 -->        <property name="maxPoolSize" value="30" />            <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->        <property name="initialPoolSize" value="10"/>            <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->        <property name="maxIdleTime" value="60"/>            <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->        <property name="acquireIncrement" value="5" />            <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements              属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。              如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->        <property name="maxStatements" value="0" />            <!--每60秒检查所有连接池中的空闲连接。Default: 0 -->        <property name="idleConnectionTestPeriod" value="60" />            <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->        <property name="acquireRetryAttempts" value="30" />            <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效              保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试              获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->        <property name="breakAfterAcquireFailure" value="true" /></bean> 

在Spring的配置文件中加入这个还不够,它虽然知道有这么个连接池,但是没有管理起来,我们需要增加Spring在创建SessionFactory时的参数:

<!-- 创建本地化工厂Bean,这是Spring整合Hibernate的入口 --><bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    <!--         不得不说Spring的这玩意设计的很好,有dataSource这个属性,        dataSource就对应Hibernate中的sessionFactory     -->    <property name="dataSource" ref="dataSource"></property>    <!--         Spring生成sessionFactory需要Hibernate的配置文件Hibernate.cfg.xml         classpath:hibernate.cfg.xml: 表示在类路径下查找hibernate.cfg.xml文件,语法格式classpath:配置文件    -->            <property name="configLocation" value="classpath:hibernate.cfg.xml" /></bean>

这样一来,Spring就会管理这个连接池,池也能发挥作用了.第一点需要注意的就是Hibernate的池并不好用,学习娱乐一下没有问题,不要放在真正的项目上面去.

第二点就是Action中的BusinessService需要由Spring注入进来,而不是在同一个Action中都写上相同的获取BusinessService的代码.好吧,也许这个很简单,就不用说了...

第三点.

就是当你以为第二点很简单的时候你就错了!你要知道Action是什么时候创建的,它是由Spring创建的吗?Spring会在Struts创建这个Action的时候给它注入对象吗?Struts1中的Action只有一个,且都是在服务器初始化的时候一并初始化的,想要让Spring注入这个对象,至少也要让Spring在服务器初始化的时候加载进来

ApplicationContext act = new ClassPathXmlApplicationContext("beans.xml");

这段代码只能在程序运行时加载beans.xml文件,我们要让beans.xml文件随服务器的启动而启动,就需要配置上下文参数:

<context-param>      <param-name>contextConfigLocation</param-name>       <!-- 加载beans.xml文件的方法二:classpath:beans.xml -->      <param-value>/WEB-INF/classes/beans.xml</param-value>  </context-param>

这段代码放在web.xml文件中,启动web应用的时候,这些参数就被读取到了ServletContext中,,整个web应用程序都共享这个上下文参数,注意和<init-param>标签不同,<init-param>是servlet范围内的参数,只能在servlet的init()方法中取得.

好了,光有上下文参数还不够,它只是以contextConfigLocation为Key,存储在ServletContext中而已,我们要让Spring容器启动,还是得用老办法:

<!-- 通过servlet配置Spring容器随Web应用的启动而初始化 --><servlet>         <servlet-name>contextLoader</servlet-name>         <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>        <load-on-startup>2</load-on-startup></servlet>

ContextLoaderServlet 这个类加载,然后读取contextConfigLocation的数据,从而启动Spring,但是我并没有在这里指定读取哪个文件,也没有告诉它读取ServletContext中的contextConfigLocation,那么它是如何知道的呢?

public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";

ContextLoaderServlet里面维护了一个ContextLoader,上面这个字符串常量是ContextLoader中的一人属性,那么就不用再说什么了吧?Spring会自动去找这个Key.

启动Spring就没有问题了,但是这样还不够,事实上我们需要在Struts创建Action的时候把BusinessService注入进来,这就需要我们对Struts的工作机制有相当的了解.Struts在工作的时候,使用了很多Processor(处理程序),每个处理程序都只完成一个功能,我们要利用Struts的这个特点,在适当的位置加入一个处理程序,然后注入对象.不过,Spring已经帮我们完成了这个功能,毕竟它号称支持全部主流框架,我们需要用到一个处理程序,但是不用考虑实现细节,修改后的Struts-config.xml文件如下:

<struts-config>    <action-mappings>        <action path="/register"                 scope="request"                parameter="method">            <forward name="success" path="/success.jsp"></forward>                </action>    </action-mappings>    <!--         ActionServlet中所有的工作委托给RequestProcessor负责        controller:struts中ActionServlet把请求委托给processorClass属性所指定的类来完成                  * org.springframework.web.struts.DelegatingRequestProcessor该类完成           spring负责创建struts的Action对象,并注入业务层对象,剩下的工作仍由Struts完成    -->    <controller processorClass="org.springframework.web.struts.DelegatingRequestProcessor"/></struts-config>

关键就是最后一句.注意:上面action中没有type属性,因为已经不需要了,写了也没有用,这是因为只要有DelegatingRequestProcessor 这个处理程序在,Struts读取这个配置文件的时候,它就会拿path指定的值去找Spring中的bean,意味着你其他不想要这样处理的action也要这样处理,不过Spring里面也很简单:

<!-- 配置RegisterAction --><bean name="/register" class="..web.action.RegisterAction">    <property name="service" ref="service"></property></bean>

就像这样,在Spring中加上这个一个bean就可以了,注意它和action的path属性值是一样的.最后修改的RegisterAction如下:

public class RegisterAction extends DispatchAction{    private BusinessService service;
    public ActionForward save(ActionMapping mapping, ActionForm form,            HttpServletRequest request, HttpServletResponse response) {        Person person = new Person();        person.setName(request.getParameter("name"));        this.service.save(person);        return mapping.findForward("success");    }    public void setService(BusinessService service) {        this.service = service;    }}

已经不需要多余的代码了,对象由Spring注入进来,然后剩下工作由Struts完成.没有解决中文乱码的问题.不过整合已经完成了.

 


 


另外,再提供另外一种解决方案,称做"分离",可以不必让Struts和Spring耦合的这么紧.

由于web.xml文件中已经将beans.xml读取到ServletContext中了,我们可以使用工具类读取ServletContext里面beans.xml的配置信息.先将beans.xml文件中关于action的配置删掉,再将Struts-config.xml文件中的处理程序删掉,给action配上type,就样就是一个传统的action配置.现在,Struts和Spring中已经没有各自的配置项了,是不是已经解耦了呢?不过光这样还不行,我们要修改RegisterAction中的代码,只需要加入两行而已:

WebApplicationContext wct = WebApplicationContextUtils.getWebApplicationContext(this.getServlet().getServletContext());service = (BusinessService) wct.getBean("service");

第一句是利用WebApplicationContextUtils工具类获取ServletContext中beans.xml文件的配置信息,然后就可以得到bean了.这样就完成了分离.


Spring2.5+Hibernate3.5+Struts1.3的整合就至此为止了,如果本文存在什么问题,请及时提出来,感激不尽..

原创粉丝点击