框架学习之Spring 第五节 SSH整合开发[Spring2.5+Hibernate3.3+Struts2]
1.首先整合Spring和Hibernate
①引入jar包:
hibernate核心安装包下的:
hibernate3.jar
lib\required\*.jar
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
数据库使用mysql:
添加 Mysql 驱动包 mysql-connector-java-5.1.16-bin.jar
后面还要进行单元测试,所以还有引入 junit4.jar,当然这个可以在后面由 Myeclipse 来进行,安装时已经有了这个jar包,Myeclipse会自动引入
②数据源配置的文件: jdbc.properties (放在src目录下)
driverClassName=org.gjt.mm.mysql.Driver
url=jdbc\:mysql\://localhost/hibernate
username=root
password=yinger
initialSize=1
maxActive=500
maxIdle=2
minIdle=1
③Spring中的beans.xml (示例的最终版,放在src目录下)
<?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/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <!-- 开启注解方式进行bean的管理和依赖注入 --> <context:annotation-config/> <!-- 数据源配置的文件的位置 --> <context:property-placeholder location="jdbc.properties" /> <!-- 定义数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 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> <!-- 定义sessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mappingResources"> <list> <value>com/yinger/domain/Person.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.hbm2ddl.auto=update hibernate.show_sql=true hibernate.format_sql=false </value> </property> </bean> <!-- 定义事务管理器:此处的含义就是下面的事务管理器管理由sessionFactory创建的session --> <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 开启注解方式使用事务 --> <tx:annotation-driven transaction-manager="txManager" /> <!-- Spring管理的bean --> <bean id="personService" class="com.yinger.service.impl.PersonServiceBean"></bean> </beans>
④测试 Spring 和 Hibernate 的整合是否成功
新建domain对象:Person
package com.yinger.domain; public class Person { private int id; private String name; public Person(){} public Person(String name){ this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
新建Person的映射文件:Person.hbm.xml
<?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 package="com.yinger.domain"> <class name="Person"> <id name="id"> <generator class="native" /> </id> <property name="name" /> </class> </hibernate-mapping>
新建业务层接口:PersonService
package com.yinger.service; import java.util.List; import com.yinger.domain.Person; public interface PersonService { public void save(Person p); public void update(Person p); public void delete(int personid); public Person getPerson(int personid); public List<Person> getPersons(); }
新建上面的接口的实现类:PersonServiceBean
package com.yinger.service.impl; import java.util.List; import javax.annotation.Resource; import org.hibernate.SessionFactory; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.yinger.domain.Person; import com.yinger.service.PersonService; @Transactional public class PersonServiceBean implements PersonService{ private SessionFactory sessionFactory; @Resource //属性的setter方法注入对象 public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } //保存 public void save(Person p) {//建议使用persist方法,而不是save方法 sessionFactory.getCurrentSession().persist(p); } //更新 public void update(Person p) {//建议使用merge方法(把对游离态的对象的更新与数据库同步),而不是update方法 sessionFactory.getCurrentSession().merge(p); } //删除 public void delete(int personid) {//建议得到对象的方法使用load,免去数据封装的过程 sessionFactory.getCurrentSession().delete(sessionFactory.getCurrentSession().load(Person.class, personid)); } //得到指定id的person @Transactional(propagation=Propagation.NOT_SUPPORTED) public Person getPerson(int personid) { return (Person)sessionFactory.getCurrentSession().get(Person.class, personid); } //得到所有的person @Transactional(propagation=Propagation.NOT_SUPPORTED) @SuppressWarnings("unchecked") public List<Person> getPersons() { return (List<Person>)sessionFactory.getCurrentSession().createQuery("from Person").list(); } }
基于PersonServiceBean 进行单元测试,编写测试类
package junit.test; import java.util.List; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.yinger.domain.Person; import com.yinger.service.PersonService; public class SSHTest { public static PersonService personService; @BeforeClass //这个方法是在单元测试实例生成之后就会执行的方法,可以在里面写上一些初始化操作 public static void setUpBeforeClass() throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); personService = (PersonService)ctx.getBean("personService"); } @Test public void testSave() { for(int i=0;i<5;i++){ Person person = new Person("personname"+(i+1)); personService.save(person); } } @Test public void testUpdate() { Person person = personService.getPerson(2); person.setName("yinger"); personService.update(person); } @Test public void testDelete() { personService.delete(3); } @Test public void testGetPerson() { personService.getPerson(4); } @Test public void testGetPersons() { List<Person> list = personService.getPersons(); for(Person person:list){ System.out.println(person.getName()); } } }
首先测试第一个方法,输出结果:
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person (name) values (?)
Hibernate: insert into Person (name) values (?)
数据库中的数据
测试delete方法:
Hibernate: select person0_.id as id0_0_, person0_.name as name0_0_ from Person person0_ where person0_.id=? Hibernate: delete from Person where id=?
测试update方法: 【我不清楚为什么这里会有两条select语句,应该是一条就可以了的啊,是不是merge方法的原因呢?望高手指点!】
Hibernate: select person0_.id as id0_0_, person0_.name as name0_0_ from Person person0_ where person0_.id=? Hibernate: select person0_.id as id0_0_, person0_.name as name0_0_ from Person person0_ where person0_.id=? Hibernate: update Person set name=? where id=?
测试getPersons方法(getPerson就不用了)
Hibernate: select person0_.id as id0_, person0_.name as name0_ from Person person0_ personname1 yinger personname4 personname5
数据库数据保持不变
从上面的测试中,我们可以看出,Spring 和 Hibernate 整合成功了!
接下来做 Hibernate中二级缓存的配置
在beans.xml中关于sessionFactory的配置中添加以下设置,最后如下所示:(假设二级缓存使用的是EhCacheProvider )
<property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.hbm2ddl.auto=update hibernate.show_sql=true hibernate.format_sql=false hibernate.cache.use_second_level_cache=true hibernate.cache.use_query_cache=false hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider </value> </property>
然后,自然要在Hibernate下载包中找到与EhCache缓存相关的jar包, 添加进去,然后找到EnCache的配置文件:encache.xml
<?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.yinger.domain.Person" maxElementsInMemory="100" eternal="false" overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="600" diskPersistent="false"/> </ehcache>
如上所示,cache标签中就可以添加需要使用二级缓存的类了
当然,也可以在类的映射文件中添加,例如在 Person.hbm.xml 中添加:
<class name="Person"> <cache usage="read-write" region="com.yinger.domain.Person"/> <id name="id"> <generator class="native" /> </id> <property name="name" /> </class>
region的解释:Hibernate为每个实体类创建一个Region作为缓存区, 默认情况下使用类的全路径名做为这个Region的名称
这样同样可以使得Person可以使用二级缓存
2.整合Spring 和 Struts2
①引入jar包:
使用到struts2的lib目录下所有不带-plugin结尾的jar文件,但除了struts2-spring-plugin-2.0.11.1.jar
②在web容器中实例化spring容器和配置struts2
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- 指定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> <!-- 配置struts2:注意struts1和struts2中的filter-class配置是不同的 --> <filter> <filter-name>struts2</filter-name> <!-- <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> --> <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
③配置struts2,编写struts.xml,我这里是包含了另外两个xml文件:
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> <include file="strutsconstant.xml"></include> <include file="strutspackage.xml"></include> </struts>
strutsconstant.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> <!-- 配置一些常量值 constant --> <!-- 通常,struts2按如下搜索顺序加载struts2常量: struts-default.xml struts-plugin.xml struts.xml struts.properties web.xml 如果在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值. --> <!-- 指定默认编码集,作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出 --> <constant name="struts.i18n.encoding" value="UTF-8"/> <!-- 该属性指定需要Struts 2处理的请求后缀,该属性的默认值是action,即所有匹配*.action的请求都由Struts2处理 如果用户需要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开 --> <constant name="struts.action.extension" value="action,do"/> <!-- 设置浏览器是否缓存静态内容,默认值为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" /> <!-- 与spring集成时,指定由spring负责action对象的创建 --> <constant name="struts.objectFactory" value="spring" /> <!-- 该属性设置Struts 2是否支持动态方法调用,该属性的默认值是true。如果需要关闭动态方法调用,则可设置该属性为false --> <constant name="struts.enable.DynamicMethodInvocation" value="false"/> </struts>
strutspackage.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> <package name="person" namespace="/person" extends="struts-default"> <global-results> <result name="message">/WEB-INF/page/message.jsp</result> </global-results> <!-- 这里使用了通配符,调用的方法名正是通配符中的字符串,class是交给Spring管理的bean(class名是bean的name或者id) --> <action name="action_*" class="personAction" method="{1}"> <result name="list">/WEB-INF/page/persons.jsp</result> </action> </package> </struts>
④在Spring的bean管理中添加一个新的bean:
<bean id="personAction" class="com.yinger.web.action.PersonAction"></bean>
注意:这个bean的id 对应struts中定义的action中的class名称
以下是测试:
⑤新建action:PersonAction
package com.yinger.web.action; import java.util.List; import javax.annotation.Resource; import com.yinger.domain.Person; import com.yinger.service.PersonService; public class PersonAction { //由于这个类已经交给了Spring管理,所以可以使用Spring的依赖注入功能注入personService //并且personService也已经交给了Spring管理 @Resource private PersonService personService; private String message; private List<Person> persons; public String list(){ this.persons = personService.getPersons(); return "list"; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public List<Person> getPersons() { return persons; } public void setPersons(List<Person> persons) { this.persons = persons; } }
⑥新建两个jsp,在WEB-INF目录下的page文件夹中
message.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP 'message.jsp' starting page</title> </head> <body> <s:property value="message"/> </body> </html>
persons.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>人员列表</title> </head> <body> <s:iterator value="persons"> id=<s:property value="id"/>---name=<s:property value="name"/><br/> </s:iterator> </body> </html>
好了,一切OK!部署应用,然后启动Tomcat服务器(我的是Tomcat 6.0),最好要保证控制台没有报错,不然就要检查!
2011-8-26 21:59:00 org.apache.catalina.core.AprLifecycleListener init
信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path:
D:\Program Files\MyEclipse Blue Edition\Common\binary\com.sun.java.jdk.win32.x86_1.6.0.013\bin;D:\Program Files\Tomcat 6.0\bin 2011-8-26 21:59:00 org.apache.coyote.http11.Http11Protocol init 信息: Initializing Coyote HTTP/1.1 on http-8080 2011-8-26 21:59:00 org.apache.catalina.startup.Catalina load 信息: Initialization processed in 1408 ms 2011-8-26 21:59:00 org.apache.catalina.core.StandardService start 信息: Starting service Catalina 2011-8-26 21:59:00 org.apache.catalina.core.StandardEngine start 信息: Starting Servlet Engine: Apache Tomcat/6.0.16 2011-8-26 21:59:04 org.apache.catalina.core.StandardContext addApplicationListener 信息: The listener "org.springframework.web.context.ContextLoaderListener" is already configured for this context. The duplicate definition has been ignored. log4j:WARN No appenders could be found for logger (org.springframework.core.CollectionFactory). log4j:WARN Please initialize the log4j system properly. 2011-8-26 21:59:05 org.apache.catalina.core.ApplicationContext log 信息: Initializing Spring root WebApplicationContext 2011-8-26 21:59:19 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info 信息: Parsing configuration file [struts-default.xml] 2011-8-26 21:59:19 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info 信息: Unable to locate configuration files of the name struts-plugin.xml, skipping 2011-8-26 21:59:19 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info 信息: Parsing configuration file [struts-plugin.xml] 2011-8-26 21:59:19 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info 信息: Parsing configuration file [struts.xml] 2011-8-26 21:59:21 org.apache.catalina.core.StandardContext addApplicationListener 信息: The listener "wdlab.base.BSLoginListener" is already configured for this context. The duplicate definition has been ignored. 2011-8-26 21:59:22 org.apache.coyote.http11.Http11Protocol start 信息: Starting Coyote HTTP/1.1 on http-8080 2011-8-26 21:59:22 org.apache.jk.common.ChannelSocket init 信息: JK: ajp13 listening on /0.0.0.0:8009 2011-8-26 21:59:22 org.apache.jk.server.JkMain start 信息: Jk running ID=0 time=0/84 config=null 2011-8-26 21:59:22 org.apache.catalina.startup.Catalina start 信息: Server startup in 21866 ms
打开IE,测试结果: 成功的输出了刚才的数据库中的数据!
SSH整合成功了!Congratulations!
最终的项目目录结构如下图所示: