故事: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。