故事:Struts的公司,在开发了Struts1之后,一直发展到大概Struts1.4,struts
与webwork的开发团队合并的原因,然后才开发了Struts2.x系列,因为struts2实质上
是得益于webwork的架构,而不是源于struts1。
一.Spring + Hibernate
导入双方的包:这个不做多提,前面Spring + Hibernate + Struts 有。
设计实体类(表):
package com.zyy.bean; public class Person { private Integer id; private String name; public Person() { } public Person(String name) { this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
实体类对应的配置文件(Person.hbm.xml):
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.zyy.bean"> <class name="com.zyy.bean.Person"> <!-- 配置ehcache缓存 --> <cache usage="read-write" region="com.zyy.service.Person"></cache> <id name="id"> <generator class="native"></generator> </id> <property name="name"></property> </class> </hibernate-mapping>
创建对应的ehcache缓存配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!-- defaultCache节点为缺省的缓存策略 maxElementsInMemory 内存中最大允许存在的对象数量 eternal 设置缓存中的对象是否永远不过期 overflowToDisk 把溢出的对象存放到硬盘上 timeToIdleSeconds 指定缓存对象空闲多长时间就过期,过期的对象会被清除掉 timeToLiveSeconds 指定缓存对象总的存活时间 diskPersistent 当jvm结束是是否持久化对象 diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间 --> <ehcache> <diskStore path="D:\cache"/> <defaultCache maxElementsInMemory="1000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="180" diskPersistent="false" diskExpiryThreadIntervalSeconds="60"/> <cache name="com.zyy.service.Person" maxElementsInMemory="100" eternal="false" overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="600" diskPersistent="false"/> </ehcache>
然后配置Spring的beans.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- 配置自动扫描bean 包含注解 --> <context:component-scan base-package="com.zyy.service"></context:component-scan> <context:component-scan base-package="com.zyy.control"></context:component-scan> <!-- 配置AOP --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 配置资源文件 --> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!-- 配置数据源 --> <bean id="dataSource" class="${dataSource}" destroy-method="close"> <property name="driverClassName" value="${driverClassName}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> <!-- 连接池启动时的初始值 --> <property name="initialSize" value="${initialSize}"/> <!-- 连接池的最大值 --> <property name="maxActive" value="${maxActive}"/> <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 --> <property name="maxIdle" value="${maxIdle}"/> <!-- 最小空闲值.当空闲的连接数少于阀值时,连接池就会预申请去一些连接,以免洪峰来时来不及申请 --> <property name="minIdle" value="${minIdle}"/> </bean> <!-- 配置Hibernate二级缓存 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <value>com/zyy/bean/Person.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.hbm2ddl.auto=update hibernate.show_sql=false hibernate.format_sql=false <!-- 配置hibernate二级缓存 --> hibernate.cache.use_second_level_cache=true <!-- 不配置查询缓存 --> hibernate.cache.use_query_cache=false <!-- 配置EhCache --> hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider </value> </property> </bean> <!-- 配置事务 --> <!-- 这里使用Spring的bean和Spring对Hibernate支持的HibernateTransactionManager --> <!-- 属性注入的是Hibernate的二级缓存 --> <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <!-- 打开事务注解的支持 --> <tx:annotation-driven transaction-manager="txManager"/> </beans>
创建beans.xml所需的资源文件jdbc.properties:
dataSource = org.apache.commons.dbcp.BasicDataSource driverClassName = org.gjt.mm.mysql.Driver url = jdbc:mysql://localhost:3306/ceshi_1?useUnicode=true&characterEncoding=UTF-8 username = root password = root initialSize = 1 maxActive = 500 maxIdle = 2 minIdle = 1
建立数据库操作的 接口 和 实现bean :
PersonService
package com.zyy.service; import com.zyy.bean.Person; import java.util.List; public interface PersonService { /** * 保存对象 * * @param peroson */ public void save(Person peroson); /** * 更新对象 * * @param peroson */ public void update(Person peroson); /** * 删除单个对象 * * @param personId */ public void delete(Integer personId); /** * 取得单个对象 * * @return */ public Person getPerson(Integer personId); /** * 取得全部对象 * * @return */ public List<Person> getPersons(); }
PersonServiceBean
package com.zyy.service.impl; import com.zyy.bean.Person; import com.zyy.service.PersonService; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Service("personService") @Transactional public class PersonServiceBean implements PersonService { /** * 将Autowired转换为按 名称匹配 * 就加上 @Qualifier("sessionFactory") * 按名称查找sessionFactory 为名字的bean */ @Autowired @Qualifier("sessionFactory") private SessionFactory sessionFactory; public void save(Person peroson) { this.sessionFactory.getCurrentSession().persist(peroson); } public void update(Person peroson) { this.sessionFactory.getCurrentSession().merge(peroson); } public void delete(Integer personId) { this.sessionFactory.getCurrentSession().delete( this.sessionFactory.getCurrentSession().load(Person.class, personId)); } @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public Person getPerson(Integer personId) { return (Person) this.sessionFactory.getCurrentSession().get(Person.class, personId); } @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true) public List<Person> getPersons() { //from 类名 不是from 数据库里的表名 //noinspection JpaQlInspection String hql = "from Person"; return this.sessionFactory.getCurrentSession().createQuery(hql).list(); } }
在类路径(src)下可选择性添加:
log4j.properties
log4j.rootLogger=WARN, Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.layout=org.apache.log4j.PatternLayout log4j.appender.Console.layout.ConversionPattern=(%r ms) [%t] %-5p: %c#%M %x: %m%n log4j.logger.com.genuitec.eclipse.sqlexplorer=DEBUG log4j.logger.org.apache=WARN log4j.logger.org.hibernate=WARN
最后编写一个junit4.4测试类,进行测试:
package test.com.zyy.service.impl; import com.zyy.bean.Person; import com.zyy.service.PersonService; import org.junit.Test; import org.junit.Before; import org.junit.After; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * PersonServiceBean Tester. * * @author <Authors name> * @version 1.0 */ public class PersonServiceBeanTest { private PersonService personService; @Before public void before() throws Exception { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); this.personService = (PersonService) applicationContext.getBean("personService"); } @After public void after() throws Exception { } /** * Method: save(Person peroson) */ @Test public void testSave() throws Exception { this.personService.save(new Person("CaMnter_SSH_save")); for (int i = 0; i < 15; i++) { this.personService.save(new Person("CaMnter_SSH_save_" + i)); } } /** * Method: update(Person peroson) */ @Test public void testUpdate() throws Exception { Person person = new Person("CaMnter_SSH_update"); person.setId(4); this.personService.update(person); } /** * Method: delete(Integer personId) */ @Test public void testDelete() throws Exception { this.personService.delete(6); } /** * Method: getPerson(Integer personId) */ @Test public void testGetPerson() throws Exception { Person person = this.personService.getPerson(7); System.out.println("id: " + person.getId() + " name: " + person.getName()); } /** * Method: getPersons() */ @Test public void testGetPersons() throws Exception { for (Person person : this.personService.getPersons()) { System.out.println("id: " + person.getId() + " name: " + person.getName()); } } @Test public void testCache() { Person person = this.personService.getPerson(7); System.out.println("id: " + person.getId() + " name: " + person.getName()); try { Thread.sleep(1000 * 7); System.out.println("请关闭Mysql数据库"); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第二次获取"); person = this.personService.getPerson(7); System.out.println("id: " + person.getId() + " name: " + person.getName()); } }
这里,我就测试了getPersons()方法:
如上,成了搭建 Spring + HIbernate。
二. Spring + Hibernate + Struts2
在类路径(src)下创建一个struts.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <!-- 该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理。 如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开。 --> <constant name="struts.action.extension" value="action"/> <!-- 设置浏览器是否缓存静态内容,默认值为true(生产环境下使用),开发阶段最好关闭 --> <constant name="struts.serve.static.browserCache" value="false"/> <!-- 当struts的配置文件修改后,系统是否自动重新加载该文件,默认值为false(生产环境下使用),开发阶段最好打开 --> <constant name="struts.configuration.xml.reload" value="true"/> <!-- 开发模式下使用,这样可以打印出更详细的错误信息 --> <constant name="struts.devMode" value="true"/> <!-- 默认的视图主题 --> <constant name="struts.ui.theme" value="simple"/> <!-- 这里才是真正的Struts2.x把action交付给Spring容器管理 --> <!-- 配置Action类 由Spring负责创建 --> <constant name="struts.objectFactory" value="spring"/> <package name="person" namespace="/person" extends="struts-default"> <global-results> <result name="message">/WEB-INF/jsp/message.jsp</result> </global-results> <action name="action_*" class="personList" method="{1}"> <!-- 相当于struts1.x中的forward --> <result name="list">/WEB-INF/jsp/personlist.jsp</result> <result name="add">/WEB-INF/jsp/add_person.jsp</result> </action> </package> </struts>
其中:
1.<constant name="struts.objectFactory" value="spring"/> 这里才是真正的
Struts2.x把action交付给Spring容器管理,相当于struts1.x在配置文件中定义一个
Spring 的controller一样交付Sping容器管理。
2.这里定义了一个package,所以想访问package里的action的话需要加上package的namespace
即 /person。
3.action里定义了一个class,这个就是action要交付给Spring管理时,Spring用注解
命名必须与这个class保持一致,即personList。
4.<action name="action_*" class="personList" method="{1}">如果想访问
action 就必须加上package 的namespace 和action 的name。即 /person/action_*。
这里用*指定了一个占位符,后面的method={1},提醒了第一个*表示的是方法的名字,由此可
得访问路径最终为:/person/action_(action中的方法名)。
由于Struts2的配置文件需求,在WEB-INF下创建JSP文件夹,再分别建立personlist.jsp、
message.jsp、add_person.jsp:
personlist.jsp
<%-- Created by IntelliJ IDEA. User: CaMnter Date: 2014/8/23 Time: 0:52 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title></title> </head> <body> <s:iterator value="persons" > <h4>id=<s:property value="id"></s:property> name=<s:property value="name"></s:property></h4> </s:iterator> </body> </html>
add_person.jsp
<%-- Created by IntelliJ IDEA. User: CaMnter Date: 2014/8/23 Time: 0:52 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title></title> </head> <body> <s:form action="action_add" namespace="/person"> 名称:<s:textfield name="person.name"></s:textfield> <input type="submit" value="添加"> </s:form> </body> </html>
message.jsp
<%-- Created by IntelliJ IDEA. User: CaMnter Date: 2014/8/23 Time: 0:52 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title></title> </head> <body> <s:property value="message"></s:property> </body> </html>
接下来我们设计一个符合@Service("personList") - > 与action中的class保
持一致的Action :
package com.zyy.control.action; import com.zyy.bean.Person; import com.zyy.service.PersonService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * 交给Spring管理的 Struts1.x 的action 必须与配置文件的 action 中的path一致 * * 但是 * * 交给Spring管理的 Struts2.x 的action 必须与配置文件的 action 中的class一致 */ @Service("personList") @Transactional public class PersonAction { @Autowired @Qualifier("personService") private PersonService personService; private String message; private List<Person> persons; private Person person; /** * 人员列表显示 * * @return */ public String list() { this.persons = this.personService.getPersons(); //返回到 result 的path上 return "list"; } /** * 人员添加界面 * * @return */ public String addUI() { return "add"; } /** * 人员添加 * * @return */ public String add() { this.personService.save(this.person); this.message = "添加成功"; return "message"; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } public List<Person> getPersons() { return persons; } public void setPersons(List<Person> persons) { this.persons = persons; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
以上:
1.每个方法的名字都可以表示配置文件中action_*里面的*的内容。例如:action_list、
action_addUI、action_add,都可以。不过切记,别忘了后缀.action,当然也可以
改为.do,这需要修改Strut2.x的配置文件。
2.return “String” ,这里的String 表示的是action的子节点result的name属性。
这就好比,是返回了一个ActionMapping,在Struts1.x里是需要找到forward的name
只不过Struts2.x简化了操作,十分类似,你只要以String的形式直接返回,result的name
就相当于执行ActionMapping,Struts2.x对Action和ActionMapping进行了封装,所
以简化了操作
3.注解@Service("personList")指定Action的名字时,必须和 action 的 class名字保持一致
4.Action里属性的名字,对应的各个页面的表单提交过来或者响应出去的数据的类型和名字,特
别是名字必须保持一致。
譬如:<1>.执行了list()方法后,返回result中的“list”,封装成ActionMapping
后跳转到personlist.jsp,Action的private List<Person> persons
与其打印的persons对应;
<2>. 执行了add()方法后,返回result中的“message”,封装成ActionMapping
后跳转到message.jsp,Action的private String message与其打印的
message对应;
最后,再修改WEB-INF下的web.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!-- 指定spring的配置文件,默认从web根目录寻找配置文件,我们可以通过spring提供的classpath:前缀指定从类路径下寻找 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:beans.xml</param-value> </context-param> <!-- 对Spring容器进行实例化 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 使用spring解决hibernate因session关闭导致的延迟加载例外问题 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置struts2 --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
这里,其实和SH+Struts1.x混搭基本一样,只不过这里配置的是Struts2.x。
三.测试
输入:http://localhost:8080/SSH2/person/action_list.action
这里,上面已经反复强调过了:/person = pageage的namespace、/action_list =
action_*,这里的list是PersonAction里的方法list(),用list指明了*的内容,所
以是/action_list。struts2.x的后缀不是.do ,是.actin 。当然在struts2.x的配
置文件中可以进行修改。
由于PersonAction中有一个addUI方法。所以输入:http://localhost:8080/SSH2
/person/action_addUI.action。
四.总结(对以上内容和SH+Struts2搭建的心得)
1.Spring + Hibernate + Struts2.x 比 Spring + Hibernate + Struts1.x 简
化了很多操作,没了ActiomForm,优化了Action的代码,使得Action变成了POJO类型的类。
2.切记访问路径的设置,与package的namespace 和 action的name 息息相关,一定要加上
namespace/action的name,这里namespace 一定要有前缀/,然后根据action的name是
否有*,并在跳转的时候指定对应的方法名字,最后再加上Struts2.x的后缀.action,也可以
通过更改Struts2.x配置文件改为.do。
3.Struts2.x的Action交付给Spring容器管理的时候,注解@Service("***")指定Action的
名字时,必须和 action 的 class名字保持一致。
4.切记,在Struts2.x配置文件中,真正意义上起到Struts2.x把action交付给Spring容器管理
作用的是<constant name="struts.objectFactory" value="spring"/>
5.Action中方法返回的String类型,就必须是result其中一个的name的值,这里优化了Action跳
转时的操作,String返回后,根据String的值生成ActionMapping后,就可以跳转了。
6.Action里属性的名字,对应的各个页面的表单提交过来或者响应出去的数据的类型和名字,特
别是名字必须保持一致。
譬如:<1>.执行了list()方法后,返回result中的“list”,封装成ActionMapping
后跳转到personlist.jsp,Action的private List<Person> persons
与其打印的persons对应;
<2>. 执行了add()方法后,返回result中的“message”,封装成ActionMapping
后跳转到message.jsp,Action的private String message与其打印的
message对应;
7.虽然开始有点不适应,因为代码优化的原因,但是建议多多使用SH+Struts2.x。 Struts2.x
和 Struts1.x 关系很小,Struts2是以Webwork的设计思想为核心,吸收了Struts1的优点
因此,可以认为Struts2是Struts1和Webwork。