大三学期渐末,事情也挺多的,上周就开始着手整合 SSH 框架,到现在才真正的完成,过程中碰到了许多小问题(小问题大折腾,哭脸. jpg)。本着善始善终的原则,最终把它给完成了。
本篇文章就在: win7 64 位,eclipse 最新版(2017.9),Spring4.3.13,Hibernate5.0.1,Struts2.3.34 环境下,整合 SSH。在下一篇文章,我们也来使用 Maven 来整合一下(毕竟学了就要用嘛,哈哈)。
首先先介绍一下 jar 包:
以上是我使用完整的 jar 包清单,我们分别导入了三个框架所需要的 jar 包,在导 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,对业务进行操作。
下面为了代码的可读性,博主不会将代码分块分析,很重要的将会指出,大多数的过程说明将在注释中给出:
先给出我们的基本代码:
- //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,我们获取 session 不再使用原始的方法了,而是使用 Spring 注入的方式为我们程序获取 session,具体的 SessionFactory 配置,将在后面的 Spring 配置文件给出。
- //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;
- }
- }
接下来我们看 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 为
,位于我们 Spring orm 包下,对于不同版本的 Hibernate,我们应该选用不同的整合 class。
- org.springframework.orm.hibernate5.LocalSessionFactoryBean
上面的 Spring 主配置文件中用
属性来替代了我们导入 Hibernate 的主配置文件,当然我们也可以直接导入 Hibernate 的主配置文件,不过为了简洁,我们这样比较方便。
- <property name="hibernateProperties">
关于配置文件后面对事务的管理,我们这里就不多说了,我的前几篇文章都有详细的介绍,有兴趣的同学可以去看看: 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:
首先是 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>
- <!-- 定义Struts 2的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://www.cnblogs.com/MindMrWang/p/8150048.html