SSH框架完全整合
整合环境及涉及技术:
win7 64位,eclipse最新版(2017.9),Spring4.3.13,Hibernate5.0.1,Struts2.3.34 环境下,整合SSH。在末尾,我们也来使用Maven来整合一下。
首先先介绍一下jar包:
以上是我使用完整的jar包清单,我们分别导入了三个框架所需要的jar包,在导jar包的时候需要注意以下几点:
- jar包冲突:struts2的javassist和Hibernate的javassist有冲突,我们选择最新的版本的(根据你的自身情况选择)。
- log4j,log4j-api,log4jcore,slf4j-api,slf4j-log4j12的的冲突,我们依旧看情况选择最新版本。
- c3p0jar包,我们选择最新的(框架之间有重复的记得删除重复)。
- Spring整合struts2需要导入Spring-web和struts2-spring-plugin jar包,不要丢掉。
- 数据库jar驱动jar包,根据自身使用的数据库进行选择
- 因为我这里使用Spring对事务的管理,所以还要导入事务的jar包,spring -jdbc的jar包,spring-orm,Aspectj的jar包。
以上都是我们需要特别注意的,否则整合的过程中会给你带来不小的麻烦
jar包就介绍差不多了,那我们就来开始吧:
首先看一下我的目录结构:
项目大体可以分为三层,视图层,service层,DAO层,因为我们这里没有什么业务,单纯的是调用DAO,所以可能service层和DAO层之间的区别不是很明显。
其实三个框架的整合,就是将Hibernate的session创建交给Spring,将Struts2的Action交给Spring。
(一)在Hibernate中,我们自己通过以下的一系列操作获取session:
//加载配置文件 Configuration config = new Configuration().configure(); //根据配置文件创建会话工厂 SessionFactory factory = config.buildSessionFactory(); //根据会话工厂创建会话 Session session = factory.getCurrentSession(); //创建一个事物对象 Transaction tx = session.beginTransaction(); //new 一个学生对象 Student student = new Student("小三",19,99); //将对象持久化到数据表中 session.save(student); //提交事务 tx.commit(); //关闭会话 session.close(); //关闭工厂 factory.close();
同样为了解耦,在项目中,我们不再自己手动的来获取session了,而是通过Spring来帮我们创建,并且service层中需要DAO,DAO需要session,也是Spring进行注入。
(二)在Struts2中,我们通过自己在Struts2的主配置文件中指定对应请求的Action的全限定类名,Struts2和Spring整合则是将Action的创建交给了Spring,由Spring来管理Action对象。
接下来我们就这两个方面分别整合Spring和Hibernate,Spring和Struts2,最后在Struts2 Action的execute方法中调用service,对业务进行操作。
下面为了代码的可读性,博主不会将代码分块分析,很重要的将会指出,大多数的过程说明将在注释中给出:
整合Spring和Hibernate:
先给出我们的基本代码:
//DAO接口: public interface StudentDao { void insert(Student student); void delete(Student student); void update(Student student); List<Student> selectAllStudents(); boolean selectStudentByIdAndName(String name,int age); }
//DAO的实现类,里面注入了SessionFactory对象,利用这个我们可以获取session public class StudentDaoImpl implements StudentDao{ //这里的sessionFactory由Spring进行注入 private SessionFactory sessionFactory; //所以这里需要setter方法,这里的getter方法顺带添上,如果以后需要获取sessionFactory的话可以调用 public SessionFactory getSessionFactory() { return sessionFactory; } //依赖注入,需要setter方法 public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } @Override public void insert(Student student) { sessionFactory.getCurrentSession().save(student); } @Override public void delete(Student student) { sessionFactory.getCurrentSession().delete(student); } @Override public void update(Student student) { sessionFactory.getCurrentSession().update(student); } @Override public List<Student> selectAllStudents() { String hql = "from Student"; return sessionFactory.getCurrentSession().createQuery(hql).list(); } //通过name和age来判别学生是否存在 @Override public boolean selectStudentByIdAndName(String name, int age) { String hql = "from Student where name=? and age=?"; boolean flag = false; if(sessionFactory.getCurrentSession().createQuery(hql).setString(0, name).setInteger(1, age).uniqueResult()!=null) { flag = true; } return flag; } }
上面的DAO,我们获取session不再使用原始的方法了,而是使用Spring注入的方式为我们程序获取session,具体的SessionFactory配置,将在后面的Spring配置文件给出。
接下来我们看Service:
//service接口 public interface StudentService { void add(Student student); void remove(Student student); void modify(Student student); List<Student> findAllStudents(); boolean findByNameAndAge(String name,int age); }
//service实现类 public class StudentServiceImpl implements StudentService { //这里的Dao对象是由Spring注入,下面要有setter方法 private StudentDao studentdao; public StudentDao getStudentdao() { return studentdao; } public void setStudentdao(StudentDao studentdao) { this.studentdao = studentdao; } @Override public void add(Student student) { studentdao.insert(student); } @Override public void remove(Student student) { studentdao.delete(student); } @Override public void modify(Student student) { studentdao.update(student); } @Override public List<Student> findAllStudents() { return studentdao.selectAllStudents(); } @Override public boolean findByNameAndAge(String name, int age) { return studentdao.selectStudentByIdAndName(name, age); } }
接着便是我们Spring的配置文件(下面的配置文件是完整的配置文件,即整合ssh的完整配置文件,其实也就是在整合Hibernate的基础上注册了Action类的bean):
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 注册c3p0数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <!--处理中文乱码问题--> <property name="jdbcUrl" value="jdbc:mysql:///test?useUnicode=true&characterEncoding=utf8"/> <property name="user" value="root"/> <property name="password" value="123"/> <!-- ?useUnicode=true&characterEncoding=utf8 --> </bean> <!-- 注册sessionFactory --> <bean id="MysessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!--不要缺少classpath,否则在整合Struts2时候会找不到映射文件--> <property name="mappingDirectoryLocations" value="classpath:com/testSpring/Entity"/> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">true</prop> <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</prop> </props> </property> </bean> <!-- 注册studentDao --> <bean id="StudentDao" class="com.testSpring.Dao.StudentDaoImpl"> <property name="sessionFactory" ref="MysessionFactory"></property> </bean> <!-- 注册studentService --> <bean id="studentservice" class="com.testSpring.Service.StudentServiceImpl"> <property name="studentdao" ref="StudentDao"/> </bean> <!-- 将Action交由Spring来管理 ref里面的studentservice引用的是上面的bean,这个是多例的,因为每个请求对应一个Action,不能多个用户共用一个Action--> <bean id="RegisterAction" class="com.testSpring.Action.RegisterAction" scope="prototype"> <property name="studentservice" ref="studentservice"/> </bean> <!-- 注册事务管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="MysessionFactory"/> </bean> <!-- 注册事务通知 --> <tx:advice id="myAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add" isolation="DEFAULT" propagation="REQUIRED"/> <tx:method name="remove" isolation="DEFAULT" propagation="REQUIRED" /> <tx:method name="modify" isolation="DEFAULT" propagation="REQUIRED" /> <tx:method name="findAllStudents" isolation="DEFAULT" propagation="REQUIRED" read-only="true"/> <tx:method name="findByNameAndAge" isolation="DEFAULT" propagation="REQUIRED" read-only="true"/> </tx:attributes> </tx:advice> <!-- aop配置切入点 --> <aop:config> <aop:pointcut expression="execution(* *..Service.*.*(..))" id="myPointCut"/> <aop:advisor advice-ref="myAdvice" pointcut-ref="myPointCut"/> </aop:config> </beans>
整合Hibernate,
我们需要注册SessionFactory,class为org.springframework.orm.hibernate5.LocalSessionFactoryBean
,位于我们Spring orm包下,对于不同版本的Hibernate,我们应该选用不同的整合class。
上面的Spring主配置文件中用<property name="hibernateProperties">
属性来替代了我们导入Hibernate的主配置文件,当然我们也可以直接导入Hibernate的主配置文件,不过为了简洁,我们这样比较方便。
关于配置文件后面对事务的管理,我们这里就不多说了,我的前几篇文章都有详细的介绍,有兴趣的同学可以去看看:
http://blog.csdn.net/qq_39266910/article/details/78826171
如果做到上面的这些,我们便可以进行测试了:
//测试类 public class Test01 { private StudentService service; @Before public void before() { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); service = (StudentService)ac.getBean("studentservice"); } @Test public void test01() { service.add(new Student("中文",18)); System.out.println("Success"); } @Test public void test02() { Student student = new Student(); student.setId(1); service.remove(student); } @Test public void test03() { Student student = new Student("张三",25); student.setId(10); service.modify(student); } @Test public void test06() { System.out.println(service.findAllStudents()); } @Test public void test07() { System.out.println(service.findByNameAndAge("中", 18)); } }
以上就是Spring整合Hibernate的全过程,接下来我们来整合Struts2:
Spring整合Struts2:
首先是Struts2的主配置文件:
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd"> <struts> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/" extends="struts-default" > <!--下面的全限定类名可以改为RegisterAction,当我们在Spring中注册当前Action类的bean--> <action name="register" class="com.testSpring.Action.RegisterAction"> <result name="success">/welcome.jsp</result> <result name="error">/error.jsp</result> </action> </package> </struts>
接着是对应的Action:
public class RegisterAction extends ActionSupport{ /** * */ private static final long serialVersionUID = 1L; private String name; private int age; //Spring会为我们自动注入service,但是这个属性名要和Spring主配置文件里面注册的studentservice的id保持一致。 //或者将Action交由Spring管理,在Spring配置Action的bean,为bean注入service,如果这样,我们在struts2主配置文件的class就不必写成Action的全限定类名,而是Spring中注册的id。 private StudentService studentservice; public RegisterAction() { super(); } public RegisterAction(String name, int age) { super(); this.name = name; this.age = age; } // 注入Service,我们需要保留set方法 public void setStudentservice(StudentService studentservice) { this.studentservice = studentservice; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String execute() { System.out.println(studentservice); System.out.println(name); System.out.println(age); studentservice.add(new Student(name,age)); return SUCCESS; } }
以上是模拟完成一个注册功能,view层发送一个请求,包含姓名和年龄,后台负责接收,并调用service层进行处理,service层调用DAO,DAO调用SessionFactory获取session,最终达到对数据库的操作。
如果仅仅这样你是不是忘了些什么?
①我们需要在web.xml中添加Struts2的核心过滤器。
②设置一个监听器,监听当web容器创建的时候,即创建我们的Spring容器,这样我们不再需要自己加载Spring的主配置文件。
③设置web容器全局参数,自定义Spring主配置文件的位置和命名
具体的看web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app 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_3_0.xsd" version="3.0"> <!-- 自定义Spring主配置文件的位置 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 使用ContextLoaderListener初始化Spring容器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 定义Struts2的FilterDispathcer的Filter --> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <!-- FilterDispatcher用来初始化Struts 2并且处理所有的WEB请求。 --> <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>
我们一并给出jsp页面(success和error页面就不写了,一个形式):
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>register</title> </head> <body> <form action="register" method="post"> 姓名<input type="text" name="name"><br> 年龄<input type="text" name="age"><br> <input type="submit" value="注册"> </form> </body> </html>
接下来我们进行测试:
点击后,跳转到:
最后看看数据库:
以上的ssh整合大体上是没有什么问题的,但是碰上延时加载的话会出现一些意想不到的事情,在讲Hibernate的session的时候,我们说过session有两种获取的方式,一个是getCurrentSession,另一个是openSession,它们两个获取的session的区别是,getSession获得的session必须要在事务中执行,也就说没有事务是不能获取session的,当我们使用session.load进行查询的时候,这就是一个延时加载,执行加载方法的时候会产生一个代理,这个代理是一个空代理,只有当我们真正需要这个代理的详细数据的时候,才会真正的进行查询,但是当它真正的查询的时候,已经没有了事务(因为我们这里的事务是通过Spring整合AspectJ,通过AOP的方式实现添加事务的),所以这个时候也就没有了session,所以当再执行详情查询的时候就会报错(no session)。
所以我们需要在web.xml中添加一个过滤器,来获取session,这个过滤器的名字叫做OpenSessionInViewFilter,添上这个过滤器后,当我们进行延时加载的话,就不会再出现no session的情况了!
在OpenSessionInViewFilter的源码中,获取session是利用的SessionFactory,也就是我们自己在Spring的注册的SessionFactory,且在里面,这个类有一个默认的SessionFactory名字就叫做sessionFactory:
注意:添加这个过滤器,一定要在Struts2的核心过滤器之前!
具体原因是:Struts2的核心过滤器中,当有Action请求的时候,会执行executeAction方法,即执行Action,不会有chain.doFilter(执行下一个过滤器),有源码有真相:
这里的mapping就是对应的action请求。
下面是openSessionInViewFilter的具体配置方法,初始化参数是为了自定义我们的sessionFactory的bean id,因为openSessionInViewFilter里面有setter方法,可以为之前设置好的默认值进行修改。
总结:当代码都写出来了,觉得很简单,但是这过程中一直小bug不断(当然大多数都是由自己的粗心造成的),其实三个框架之间的真核无非就是将所有关于类的创建管理交由Spring,由Spring来为需要的注入所需要的bean,不再需要手动的创建一个个的类,使得各个层级之间耦合度降低,即使一层代码出现了问题不需要修改另一层的代码,便于我们项目的维护和更新,也便于出现问题能够即使定位出错的位置。
版权声明:本文为博主原创文章,如需转载请表明出处。 https://blog.csdn.net/qq_39266910/article/details/78926884