Java第三阶段之Spring整合SSH

整合Struts2、Hibernate4.3和Spring4.2

整合三大框架得先从搭建各部分环境开始,也就是说首先得把Spring,Hibernate和Struts2的环境搭建好,确保它们没有问题了,再做整合。这篇博文遵从的顺序是:先搭建Spring环境–>然后搭建Hibernate环境–> 整合Spring和Hibernate --> 搭建Struts2环境 --> 整合Spring和Struts2。

1整个项目jar包的管理
2搭建Spring环境
2.1添加配置文件beansxml和相应的jar包
2.2测试Spring的IoC环境
3搭建Hibernate环境
3.1添加相应的jar包
3.2新建数据库和表
3.3DB浏览器连接到Mysql数据库
3.4创建xml映射文件和sessionFactory
3.5通过逆向工程生成model、orm映射文件
3.6测试Hibernate持久化数据库
4整合Spring和Hibernate
4.1导入相应的jar包
4.2配置数据源dataSource
4.3配置sessionFactory
4.4配置事务管理器
4.5配置advice通知
4.6配置AOP切面
4.7测试整合结果
5搭建Struts2环境
5.1添加相应的配置和jar包
5.2创建Action并且配置到strutsxml文件中
5.3测试Struts2环境
6Spring和Struts2整合
6.1添加相应的jar包
6.2把Action和它的依赖交给Spring管理
6.3修改strutsxml中的配置
6.4配置监听器
6.5测试整合结果

1整个项目jar包的管理

Spring的jar包很多,开发的时候建议将它们分个类,然后依次添加到User Library,方便管理,也一目了然。这里我总结一下整个SSH所需要的基本jar包,看下图:
在这里插入图片描述
从图中可以看出,首先将Spring的jar分为四类:spring-4.2.4-core、spring-4.2.4-aop、spring-4.2.4-persistence以及spring-4.2.4-web。将spring的核心包都放到core中,与aop相关的都放到aop中,与持久化(与Hibernate整合)相关的放到persistence中,与web(与struts2整合)相关的放到web中。每个部分都有哪些jar包呢?请看下面的截图:
在这里插入图片描述
在这里插入图片描述
注:以上每个分类的包中,并非包含原来包中所有的jar,有些jar文件并没有用到,等具体项目需要的时候再往里加就行了,上图是保证项目的环境可以搭建所需要的的最基本的jar包。

2搭建Spring环境

上面的jar包截图是最后整合好的所有jar包,刚开始搭建环境的时候不需要一次性全部加进来,可以一点一点的加,这样也更利于理解每个部分的jar包都有什么作用,当然,一次都加进来也是可以的。

2.1添加配置文件beansxml和相应的jar包

新建一个工程,然后添加在User Library中添加自己的库,这里主要添加两个,即spring-4.2.4-core和spring4.2.4-aop,添加jar包不再赘述。添加完了后,在src目录下添加beans.xml文件,这个文件的模板网上很多,Spring自带的例子里也有,考一份过来就行,见下图:
在这里插入图片描述

2.2测试Spring的IoC环境

我们写一个普通的java类java.util.Date类来测试一下Spring IoC是否正常,如果在测试程序中能正常注入,则说明Spring的IoC环境搭建成功,下面我们写一个测试用例:

/**
 * @Description TODO(采用Spring的注解调试,仅仅支持Spring3.1及以上)
 * @author Ni Shengwu
 *
 */
/*
 * Spring3.1后多了个spring-test-4.2.4.RELEASE.jar包,这个jar包专门用来支持JUnit基于注解的测试的,该jar包在spring-4.2.4-core中
 * 该jar包里有个SpringJUnit4ClassRunner.class,用@RunWith注解加进来即可
 * 
 * 注解@ContextConfiguration表示将ApplicationContext对象注入进来,就不用像以往那样在测试程序里先new了,直接使用
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:beans.xml")
public class SSHTest {
	
	@Resource
	private Date date;
	
	@Test //测试Spring IOC的开发环境
 	public void springIoc() {
		System.out.println(date);
	}
}

最后能正常输出:Thu Apr 28 22:45:13 CST 2016。说明Date对象已经被Spring给注入进来了,从而验证了Spring IoC功能正常,为此,Spring的环境搭建完成。

3搭建Hibernate环境

Hibernate的环境搭建相比于Spring要复杂一些,因为用到了MyEclipse中的逆向工程。我们按以下步骤来搭建Hibernate开发环境:

3.1添加相应的jar包

这里主要是向User Library中添加两项jar包:hibernate4.3.11和MySQL驱动包mysql-connector-java-5.1.26,不再赘述。

3.2新建数据库和表

drop database if exists shop;
create database shop default character set utf8;
use shop;
drop table if exists category;
create table category
(
   /* 类别编号,自动增长 */
   id  int not null auto_increment,
   /* 类别名称 */
   type varchar(20),
   /* 类别是否为热点类别,热点类别才有可能显示在首页*/
   hot  bool default false,
   /* 设置类别编号为主键*/
   primary key (id)
);

3.3DB浏览器连接到Mysql数据库

DB浏览器指的是MyEclipse中的一个视图窗口,可以很直观的看到MySQL中都有哪些数据库和表,打开DB浏览器的方法:Window->Open Perspective->DB Browser打开DB浏览器工作窗口。如果没有DB Browser,按照下面:Window->Show View->other->输入DB Browser,找到打开即可。

打开后,我们开始连接到MySQL数据库了,在DB Browser窗口的空白处右键->new,就会弹出下面的对话框:
在这里插入图片描述
填好后,点击Test Driver测试一下,测试通过表示DataBase connection Driver已经配置好,然后finish即可,这样我们就能在DB浏览器窗口中看到数据库MySQL5.6了,右键打开就能看到数据库中已有的库和表了,如下:
在这里插入图片描述

3.4创建xml映射文件和sessionFactory

sessionFactory是用来创建session的,我们通过如下方式创建:工程名右键->myeclipse->Add Hibernate Capabilities,如果没有Add Hibernate Capabilities,就点击project facets->Install Hibernate Facets,会弹出如下窗口:
在这里插入图片描述
下一步,在MyEclipse中添加Hibernate Support,即hibernate.cfg.xml映射文件和sessionFactory。这里主要是给sessionFactory建个包,默认包不行。
在这里插入图片描述
下一步,添加驱动,由于我们之前已经配置好了一个驱动了,所以在这里直接选择刚刚配置好的即可。
在这里插入图片描述
下一步,由于之前我们已经添加过自己的jar保留,这里不用选择,直接finish。
在这里插入图片描述
这样我们就完成了Hibernate配置文件和sessionFactory的创建。下面我们简单看一下MyEclipse创建的sessionFactory里面都有啥:

public class HibernateSessionFactory {
 
    private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>(); //sessionFactory中用的是线程池技术
    private static org.hibernate.SessionFactory sessionFactory; //sessionFactory:创建session的工厂
	
    private static Configuration configuration = new Configuration();
    private static ServiceRegistry serviceRegistry; 
 
    static { //类加载时初始化sessionFactory
    	try {
		configuration.configure();
		serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
		sessionFactory = configuration.buildSessionFactory(serviceRegistry); //Hibernate4中创建sessionFactory的方法
	} catch (Exception e) {
		System.err.println("%%%% Error Creating SessionFactory %%%%");
		e.printStackTrace();
	}
    }
    private HibernateSessionFactory() { //私有构造方法阻止new出对象,保证sessionFactory的单例
    }
	
    public static Session getSession() throws HibernateException {
        Session session = (Session) threadLocal.get();  //从线程池中拿session
 
		if (session == null || !session.isOpen()) { //如果线程池是空的,或者session打开失败
			if (sessionFactory == null) {
				rebuildSessionFactory(); //如果sessionFactory是空的,那就再创建一次,和static部分一样的
			}
			session = (sessionFactory != null) ? sessionFactory.openSession() : null; //sessionFactory不为空就创建一个session
			threadLocal.set(session); //然后把这个session放到线程池中,下次再拿
		}
 
        return session;
    }
 
    public static void rebuildSessionFactory() {
	try {
		configuration.configure();
		serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry();
		sessionFactory = configuration.buildSessionFactory(serviceRegistry);
	} catch (Exception e) {
		System.err.println("%%%% Error Creating SessionFactory %%%%");
		e.printStackTrace();
	}
    }
 
    public static void closeSession() throws HibernateException {
        Session session = (Session) threadLocal.get();
        threadLocal.set(null);
 
        if (session != null) {
            session.close();
        }
    }
 
    public static org.hibernate.SessionFactory getSessionFactory() {//提供一个公共接口让外界获得这个单例sessionFactory
	return sessionFactory;
    }
 
    public static Configuration getConfiguration() {
	return configuration;
    }
 
}

从创建的HibernateSessionFactory中可以看出,主要用到了单例模式和线程池技术。也不难理解。

3.5通过逆向工程生成model、orm映射文件

下面开始使用逆向工程创建实例对象了,也就是数据库的表对应的model。在DB Browsera窗口右击我们刚刚创建的表shop,选择Hibernate Reverse Engineering开始创建:
在这里插入图片描述
创建的时候有两种方式,基于配置文件的和基于注解的,具体的看开发人员心情了,可以选择:
在这里插入图片描述
然后下一步,选择一下native主键自增方式,然后便完成了逆向工程创建model和orm映射了。
在这里插入图片描述
完成后,会有Category的model生成,同时在hibernate.cfg.xml文件中也会生成相应的映射,前面基于配置文件的和基于注解的在hibernate.cfg.xml中生成的映射时不同的。

3.6测试Hibernate持久化数据库

因为这里还没有与Spring整合,只是单纯的搭建Hibernate开发环境,所以我们没有必要使用注解,我们通过直接new一个service的方式执行数据入库。

先写CategoryService接口和实现类:

public interface CategoryService {
	public void save(Category category); //用来测试Hibernate环境
}
 
public class CategoryServiceImpl implements CategoryService {
 
    @Override //没有和Spring整合的情况
    public void save(Category category) {
        //通过刚刚生成的sessionFactory获取session
        Session session = HibernateSessionFactory.getSession();
        try {
            //手动事务
            session.getTransaction().begin();
            //执行业务逻辑
            session.save(category);
            //手动提交
            session.getTransaction().commit();
        } catch(Exception e) {
            session.getTransaction().rollback();
            throw new RuntimeException(e);
        } finally {
            HibernateSessionFactory.closeSession();
        }       
    }
}

下面在刚刚的测试用例中添加对Hibernate的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:beans.xml")
public class SSHTest {
	
	@Resource
	private Date date;	
	
	@Test //测试Spring IOC的开发环境
	public void springIoc() {
		System.out.println(date);
	}
	
	@Test  //测试Hibernate的开发环境,因为没有整合,可以直接new
	public void hihernate() {
		CategoryService categoryService = new CategoryServiceImpl();
		Category category = new Category("男士休闲", true);
		categoryService.save(category);
	}
}

我们查看数据库,发现多了刚刚插入的这项,说明Hibernate环境没有问题。至此,Hibernate的开发环境我们就搭建好了。

4整合Spring和Hibernate

搭建好了Spring和Hibernate的开发环境后,我们开始整合这两者。整合Spring和Hibernate后就可以使用AOP让Spring来管理Hibernate的事务了。整合Spring和Hibernate主要从两大方面入手,一是导入必要的jar包,二是配置beans.xml文件。下面我们一步步整合Spring和Hibernate。

4.1导入相应的jar包

整合Spring和Hibernate时需要导入的jar包有两大块,spring4.2.4-persistence和c3p0-0.9.5.1,每一块jar包中的具体jar文件请参见上面的截图,这里不再赘述。下面开始配置beans.xml文件了。

4.2配置数据源dataSource

首先配置一下dataSource,然后hibernate.cfg.xml中相应的部分可以干掉了。因为在Spring中配置好了,Spring会去初始化这个dataSource,也就是说这就交给Spring来完成了,hibernate.cfg.xml中的相应部分可以删掉了。如下:

 <!-- com.mchange.v2.c3p0.ComboPooledDataSource类在c3p0-0.9.5.1.jar包的com.mchange.v2.c3p0包中 -->
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
 	<property name="driverClass" value="com.mysql.jdbc.Driver" />
 	<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/shop" />
 	<property name="user" value="root" />
 	<property name="password" value="root" />
 </bean>

hibernate.cfg.xml中需要干掉的部分:
在这里插入图片描述

4.3配置sessionFactory

配置sessionFactory是用来产生一个session的,另外HibernateTemplate也可以,但是这里采用sessionFactory而不用HibernateTemplate,是因为HibernateTemplate是Spring提供的,依赖于Spring,如果哪天不用Spring了,就会报错。而sessionFactory是Hibernate提供的,没有问题。HibernateTemplate的依赖性太强了。下面看一下具体配置:

<!-- org.springframework.orm.hibernate4.LocalSessionFactoryBean类在spring-orm-4.2.4.RELEASE.jar包的org.springframework.orm.hibernate4包中 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
	 <property name="dataSource" ref="dataSource" />
	 <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <!-- 加载hibernate配置文件 -->
</bean>

sessionFactory中的dataSource我们用刚刚配好的dataSource,用ref属性引用进来即可,configLocation我们不在这配了,直接加载hibernate.cfg.xml文件,使用hibernate配置文件中的配置,更加简洁方便。

4.4配置事务管理器

配置事务管理器,是用来管理sessionFactory的,这样所有的由sessionFactory产生的session将会有声明式的管理。配置如下:

 <!-- org.springframework.orm.hibernate4.HibernateTransactionManager类spring-orm-4.2.4.RELEASE.jar包的org.springframework.orm.hibernate4包中 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
	 <property name="sessionFactory" ref="sessionFactory" />
</bean>

同样,sessionFactory我们用刚刚配置好的sessionFactory,用ref属性引用进来即可。到这里就会发现,从上面一直配下来,都是一连串的操作,一个个引用下来。

4.5配置advice通知

配置advice的目的是指定哪些方法需要什么类型的事务模式。看配置:

 <tx:advice id="advice" transaction-manager="transactionManager">
 	<tx:attributes>
 		<tx:method name="save*" propagation="REQUIRED"/>
 		<tx:method name="update*" propagation="REQUIRED"/>
 		<tx:method name="delete*" propagation="REQUIRED"/>
 		<tx:method name="*" propagation="SUPPORTS"/>
 	</tx:attributes>
 </tx:advice>

REQUIRED表示如果存在事务,则支持当前的事务,如果没有则创建一个新的事务,这个事务模式应用在所有以save、update和delete开头的方法上,也就是说对数据库的增删改的时候需要添加事务支持。SUPPORTS表示如果存在事务,则支持当前的事务,如果没有就算了。

4.6配置AOP切面

<aop:config>
 	<!-- 配置哪些包的类要切入事务 -->
 	<aop:pointcut id="pointcut" expression="execution(* cn.it.shop.service.impl.*.*(..))" />
 	<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/><!-- 连接了<span style="font-family:Microsoft YaHei;">上</span>面的advice和上面的pointcut -->
 	<!-- aop:pointcut要写在aop:advisor上面,否则会报错 -->
 </aop:config>

AOP即面向切面编程,aop:pointcut定义一个切面,expression属性中配置的意思是所有cn.it.shop.service.impl包下的所有方法,不管返回值和参数是什么,都要切入事务。该包是属于dao层的,直接操作数据库的。aop:advice将通知和切面结合起来,我们直接使用上面配置好的advice和pointcut,将其引入进来即可。这样配置好了后,意思就是说,凡是cn.it.shop.service.impl包下的方法都需要切入事务管理,具体地,以save、update、delete开头的方法使用REQUIED方式,其他方法使用SUPPORTS方式。这样就很好理解这个配置的意思了。

4.7测试整合结果

之前搭建Hibernate环境的时候,我们测试是直接new了一个Service来操作数据库,因为当时还没有和Spring整合。现在通过配置beans.xml后,让Spring去管理Hibernate的事务了,所以现在的测试要把Service交给Spring管理,通过Spring注入进来,并且依赖sessionFactory,如果能插入数据到数据库,则说明声明事务OK。
首先,我们要在Spring的配置文件beans.xml中配一下这个Service:

 <bean id="categoryService" class="cn.it.shop.service.impl.CategoryServiceImpl">
 	<property name="sessionFactory" ref="sessionFactory" /><!-- 依赖的sessionFactory用我们之前配好的sessionFactory-->
 </bean>

其次,我们需要在CategoryService接口和它的实现类CategoryServiceImpl中增加一个方法用来测试整合后的情况:

public interface CategoryService {
	public void save(Category category); //用来测试Hibernate环境
	public void update(Category category); //用来测试Spring和Hibernate整合后
}
 
public class CategoryServiceImpl implements CategoryService {
 
    @Override //没有和Spring整合的情况
    public void save(Category category) {
        //通过工具类获取session
        Session session = HibernateSessionFactory.getSession();
        try {
            //手动事务
            session.getTransaction().begin();
            //执行业务逻辑
            session.save(category);
            //手动提交
            session.getTransaction().commit();
        } catch(Exception e) {
            session.getTransaction().rollback();
            throw new RuntimeException(e);
        } finally {
            HibernateSessionFactory.closeSession();
        }
        
    }
    /*Spring和Hibernate整个后*/
    private SessionFactory sessionFactory; //定义一个sessionFactory
    
    //当需要使用sessoinFactory的时候,Spring会将sessionFactory注入进来
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }  
    protected Session getSession() {
        //从当前线程获取session,如果没有则创建一个新的session
        return sessionFactory.getCurrentSession();
    }
    
    @Override //Spring和Hibernate整合后的情况
    public void update(Category category) {
        getSession().update(category);    
    }
}

现在我们可以去测试类中增添测试方法,来测试Spring和Hibernate整合后的结果了:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:beans.xml")
public class SSHTest {
	
	@Resource
	private Date date;
	
	@Resource
	private CategoryService categoryService;
	
	@Test //测试Spring IOC的开发环境
	public void springIoc() {
		System.out.println(date);
	}
	
	@Test  //测试Hibernate的开发环境,因为没有整合,可以直接new
	public void hihernate() {
		CategoryService categoryService = new CategoryServiceImpl();
		Category category = new Category("男士休闲", true);
		categoryService.save(category);
	}
	
	@Test //测试Hibernate和Spring整合后
	public void hibernateAndSpring() {
		categoryService.update(new Category(1, "休闲女式", true)); //categoryService通过Spring从上面注入进来的
	}
}

然后我们查看数据库,发现id=1的category被修改成了休闲女式了,说明更新成功。至此,Spring和Hibernate整合成功。

5搭建Struts2环境

5.1添加相应的配置和jar包

struts2运行所需的jar包我放在struts2.3.41的Library中了,直接引入即可,不再赘述。另外,还要对web.xml文件进行配置,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>E_shop</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>
  
  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>*.action</url-pattern>
  </filter-mapping>
  
</web-app>

如上,我们配置了一个StrutsPrepareAndExecuteFilter过滤器,并将过滤器的url-pattern设置为*.action,即所有.action后缀的都会首先经过这个过滤器,这也是struts2的入口处。

5.2创建Action并且配置到strutsxml文件中

我们创建一个Action如下:

public class CategoryAction extends ActionSupport {
	private CategoryService categoryService; //设置categoryService是为了很直观的看出与Spring整合前后的不同
	
	public void setCategoryService(CategoryService categoryService) {
		this.categoryService = categoryService;
	}
	
	public String update() {
		System.out.println("----update----");
		System.out.println(categoryService); //整合前后输出不同
		return "index";
	}
	
	public String save() {
		System.out.println("----save----");
		System.out.println(categoryService);//整合前后输出不同
		return "index";
	}
}

然后我们配置struts.xml文件,该文件放在src目录下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
 
<struts>
	<package name="shop" extends="struts-default">
		<!-- category_update.actiocan: 访问update方法 -->
		<action name="category_*" class="cn.it.shop.action.CategoryAction" method="{1}">
			<result name="index">/index.jsp</result>
		</action>
	</package>
 
</struts>

5.3测试Struts2环境

测试方法是:写一个jsp访问Action,如果Action可以创建,则表示struts2环境OK。即struts2的一连串流程可以正常走完:jsp–>struts.xml–>Action–>struts.xml–>jsp,这样struts2的环境就算搭好了。我们写一个简单的index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>
  
  <body> 
    <!-- 下面两种写法都可以访问 --></span>
    <a href="${pageContext.request.contextPath }/category_update.action">访问update</a>
    <a href="category_save.action">访问save</a>
  </body>
</html>

然后我们部署以下工程,打开tomcat服务器,在浏览器中输入:http://localhost:8080/E_shop/index.jsp后,能出现正常jsp页面,然后点击两个按钮,仍然跳转到index.jsp,然后我们看一下控制台的输出信息:
----update----
null
----save----
null
说明struts2的一条线走完了,环境没有问题,至此,struts2开发环境搭建完毕。

我们看控制台输出了null,也就是说categoryService是空,也就是说根本没拿到categoryService,因为我们没有和Spring进行整合,没有被注入进来,所以null是正常的。我们沿着控制台输出的信息往上翻,会发现一条信息:Choosing bean (struts) for (com.opensymphony.xwork2.ObjectFactory)。括号里是struts说明没有和Spring整合前,Action是有Struts2产生的。

6Spring和Struts2整合

6.1添加相应的jar包

Spring与Struts2整合时的jar包主要在spring4.2.4-web里面,里面包括struts2-spring-plugin-2.3.24.1.jar,导包不再赘述。

6.2把Action和它的依赖交给Spring管理

在Spring的配置文件beans.xml中配置Action和它的依赖,我们目前只有一个Action,配置如下所示:

 <bean id="date" class="java.util.Date" />
 <bean id="categoryAction" class="cn.it.shop.action.CategoryAction" scope="prototype">
 	<property name="categoryService" ref="categoryService" /> <!-- 依赖的categoryService用上面和Hibernate整合时配置好的categoryService -->
 </bean>

6.3修改strutsxml中的配置

原来在struts.xml中,class属性对应的是具体Action的完全限定名,现在将class属性的值改为Spring中配置action的id值,即categoryAction,如下:

<struts>
	<package name="shop" extends="struts-default">
 
		<!-- class对应的是Spring中配置该Action的id值,因为要交给Spring管理 -->
		<action name="category_*" class="categoryAction" method="{1}">
			<result name="index">/index.jsp</result>
		</action>
	</package>
 
</struts>

6.4配置监听器

在web.xml中配置监听器ContextLoaderListener,这样在服务器启动的时候就可以加载Spring的配置文件了。如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>E_shop</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
  </filter>
  
  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>*.action</url-pattern>
  </filter-mapping>
 
 <!-- web.xml中监听器的启动优先级要高于过滤器,所以配在下面无所谓的 -->
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:beans.xml</param-value>
  </context-param>
  
</web-app>

6.5测试整合结果

我们在Action中新加一句更新数据库的语句,如下:

public class CategoryAction extends ActionSupport {
	
	private Category category;//设置一个私有成员变量接收url带过来的参数,注意下面要写好get和set方法
	
	private CategoryService categoryService;
	
	public void setCategoryService(CategoryService categoryService) {
		this.categoryService = categoryService;
	}
	
	public String update() {
		System.out.println("----update----");
		System.out.println(categoryService);//由于已经和Spring整合,所以可以拿到这个categoryService了,打印出来就不是null了
		categoryService.update(category); //新加一条语句,来更新数据库
		return "index";
	}
	
	public String save() {
		System.out.println("----save----");
		System.out.println(categoryService);
		return "index";
	}
 
	public Category getCategory() {
		return category;
	}
 
	public void setCategory(Category category) {
		this.category = category;
	}
}

然后我们修改一下index.jsp文件,如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>
  
  <body>
    <a href="${pageContext.request.contextPath }/category_update.action?category.id=2&category.type=gga&category.hot=false">访问update</a>
    <a href="category_save.action">访问save</a>
  </body>
</html>

然后我们部署以下工程,打开tomcat服务器,在浏览器中输入:http://localhost:8080/E_shop/index.jsp后,能出现正常jsp页面,然后点击“访问update”按钮,仍然跳转到index.jsp,然后我们看一下控制台的输出信息:
----update----
cn.it.shop.service.impl.CategoryServiceImpl@7c5ecf80
Hibernate: update category set hot=?, type=? where id=?
我们看到可以输出categoryService这个对象的信息了,也可以输出执行update语句时的SQL语句,然后我们查询一下数据库,发现id=2的数据的type被更新为gga,hot更新为false。我们沿着控制台输出的信息往上翻,会发现一条信息:Choosing bean (spring) for (com.opensymphony.xwork2.ObjectFactory),括号里为spring,与上面的情况对比可知,Struts2在与Spring整合后,Action交给了Spring去管理了。

至此,Struts2、Hibernate4和Spring4整合工作已经全部完成。接下来就可以在SSH环境下进行开发了!

基本增删查改、Service和Action的抽取以及使用注解替换xml

1Service层的抽取
1.1完善CategoryService层
1.2Service层抽取实现
2Service层添加一个Account
3Action的抽取
3.1Action中往域requestsessionapplication等中存数据
3.2抽取BaseAction
3.3获取参数ModelDriven
3.4抽取ModelDriven到BaseAction
3.5抽取service到BaseAction
4将xml改成注解
上一节我们搭建好了Struts2、Hibernate和Spring的开发环境,并成功将它们整合在一起。这节主要完成一些基本的增删改查以及Service、Dao和Action的抽取。

1Service层的抽取

上一节中,我们在service层简单写了save和update方法,这里我们开始完善该部分的代码,然后对service层的代码进行抽取。

1.1完善CategoryService层

对数据库的操作无非是增删改查,首先我们来完善CategoryService层的接口和实现:

 //CategoryService接口
public interface CategoryService extends BaseService<Category> {
	
	public void save(Category category); //插入
 
	public void update(Category category);//更新
	
	public void delete(int id); //删除
	
	public Category get(int id); //获取一个Category
	
	public List<Category> query(); //获取全部Category
 
}

对CategoryService接口的具体实现:

public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {
 
	private SessionFactory sessionFactory;
	
	//Spring会注进来
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	protected Session getSession() {
		//从当前线程获取session,如果没有则创建一个新的session
		return sessionFactory.getCurrentSession();
	}
	 
	@Override 
	public void save(Category category) {
		getSession().save(category);
	}
	
	@Override 
	public void update(Category category) {
		getSession().update(category);	
	}
 
	@Override
	public void delete(int id) {
		/*第一种方法有个弊端,就是没删除一次得先查询一次
		Object obj = getSession().get(Category.class, id);
		if(obj != null) {
			getSession().delete(obj);
		}*/
		String hql = "delete Category while id=:id";
		getSession().createQuery(hql) //
				.setInteger("id", id) //
				.executeUpdate();
	}
 
	@Override
	public Category get(int id) {
		return (Category) getSession().get(Category.class, id);
	}
 
	@Override
	public List<Category> query() {
		String hql = "from Category";
		return getSession().createQuery(hql).list();
	}
}

1.2Service层抽取实现

完成了CategoryService后,我们来抽取Service层的基础实现。思路是这样的:我们抽取一个基础接口BaseService以及基础接口的实现BaseServiceImpl,后面开发的时候,如果需要新的Service,只需要做两步即可:首先定义一个新的接口xxxService继承BaseService接口,这个接口可以增加新的抽象方法;然后定义一个新的实现类xxxServiceImpl继承BaseServiceImpl并实现xxxService接口即可。这样更加便于项目的维护。
我们先根据上面的CategoryService接口来创建BaseService接口:

//基础接口BaseService,使用泛型
public interface BaseService<T> {
	public void save(T t);
 
	public void update(T t);
	
	public void delete(int id);
	
	public T get(int id);
	
	public List<T> query();
}

然后再根据CategoryServiceImpl实现类创建BaseService接口的实现类BaseServiceImpl:

 /**
 * @Description TODO(公共模块的抽取)
 * @author eson_15
 *
 */
@SuppressWarnings("unchecked")
public class BaseServiceImpl<T> implements BaseService<T> {
 
	private Class clazz; //clazz中存储了当前操作的类型,即泛型T
	private SessionFactory sessionFactory;
	
	public BaseServiceImpl() {
                //下面三个打印信息可以去掉,这里是给自己看的
                System.out.println("this代表的是当前调用构造方法的对象" + this);
		System.out.println("获取当前this对象的父类信息" + this.getClass().getSuperclass());
		System.out.println("获取当前this对象的父类信息(包括泛型信息)" + this.getClass().getGenericSuperclass());
		//拿到泛型的参数类型
		ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
		clazz = (Class)type.getActualTypeArguments()[0];
	}
	
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	
	protected Session getSession() {
		//从当前线程获取session,如果没有则创建一个新的session
		return sessionFactory.getCurrentSession();
	}
	
	@Override
	public void save(T t) {
		getSession().save(t);
	}
 
	@Override
	public void update(T t) {
		getSession().update(t);	
	}
 
	@Override
	public void delete(int id) {
		System.out.println(clazz.getSimpleName());
		String hql = "delete " + clazz.getSimpleName() + " as c where c.id=:id";
		getSession().createQuery(hql) //
				  .setInteger("id", id) //
				  .executeUpdate();
	}
 
	@Override
	public T get(int id) {
		return (T) getSession().get(clazz, id);
	}
 
	@Override
	public List<T> query() {
		String hql = "from " + clazz.getSimpleName();
		return getSession().createQuery(hql).list();
	}
 
}

抽取完了后,我们就可以改写CategoryService接口和CategoryServiceImpl实现类了。如下:

//CategoryService接口继承BaseService接口
public interface CategoryService extends BaseService<Category> {
	/*
        * 只要添加CategoryService本身需要的新的方法即可,公共方法已经在BaseService中了
        */
}
 
/**
 * @Description TODO(模块自身的业务逻辑)
 * @author eson_15
 *
 */
public class CategoryServiceImpl extends BaseServiceImpl<Category> implements CategoryService {
 
    /*
     * 只需实现CategoryService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了
     */
}

从代码中可以看出,新增的Service只需要继承BaseService接口,然后在接口中新增本Service所需要的业务逻辑即可。新增的ServiceImpl只需要继承BaseServiceImpl并实现新增的业务逻辑即可。
但是别忘了很重要的一点:就是修改Spring的配置文件beans.xml中的bean。

<!-- 泛型类是不能实例化的,所以要加lazy-init属性 -->
<bean id="baseService" class="cn.it.shop.service.impl.BaseServiceImpl" lazy-init="true">
	 <property name="sessionFactory" ref="sessionFactory" />
</bean>
	 
<bean id="categoryService" class="cn.it.shop.service.impl.CategoryServiceImpl" parent="baseService"/>

将原来categoryService中的property干掉,然后增加parent属性,指明继承baseService;然后配置一下baseService,将sessionFactory配到baseService中去,另外要注意一点:设置lazy-init属性为true,因为baseService是泛型类,泛型类是不能实例化的。至此,Service层的抽取就搞定了。

2Service层添加一个Account

刚刚抽取好了Service层,那么现在我们想写一个Account(管理员)的service就很简单了:
首先写一个AccountService接口继承BaseService:

public interface AccountService extends BaseService<Account> { //注意BaseService里的泛型现在是Account
	/*
	 * 只要添加AccountService本身需要的新的方法即可,公共方法已经在BaseService中了
	 */
}

然后写一个AccountServiceImpl实现类继承BaseServiceImpl实现类,并实现AccountService接口即可:

public class AccountServiceImpl extends BaseServiceImpl<Account> implements AccountService {
 
	/*
	 * 只需实现AccountService接口中新增的方法即可,公共方法已经在BaseServiceImpl中实现了
	 */
	
	//管理登陆功能,后期再完善
}

最后在beans.xml文件里加上如下配置:

<bean id="accountService" class="cn.it.shop.service.impl.AccountServiceImpl" parent="baseService" />

这样就写好了一个新的service了,以后需要添加service就遵循这个流程,非常方便。

3Action的抽取

3.1Action中往域request/session/application等中存数据

我们知道,在Action中可以直接通过ActionContext.getContext()去获取一个ActionContext对象,然后通过该对象再去获得相应的域对象;也可以通过实现xxxAware接口来注入相应的域对象。我们先来看一下这两种方法:

public class CategoryAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware{
	
	private Category category;
 
        private CategoryService categoryService;
    
        public void setCategoryService(CategoryService categoryService) {
            this.categoryService = categoryService;
        }
 
	public String update() {
		System.out.println("----update----");
		categoryService.update(category);
 		return "index";
 	}
	
	public String save() {
		System.out.println("----save----");
		return "index";
 	}
	
 	public String query() {
		 //解决方案一,采用相应的map取代原来的内置对象,这样与jsp没有依赖,但是代码量比较大
 //		ActionContext.getContext().put("categoryList", categoryService.query()); //放到request域中
 //		ActionContext.getContext().getSession().put("categoryList", categoryService.query()); //放到session域中
 //		ActionContext.getContext().getApplication().put("categoryList", categoryService.query()); //放到application域中
		
		//解决方案二,实现相应的接口(RequestAware,SessionAware,ApplicationAware),让相应的map注入
		request.put("categoryList", categoryService.query()); 
		session.put("categoryList", categoryService.query()); 
		application.put("categoryList", categoryService.query()); 
		return "index";
	}
 
	public Category getCategory() {
		return category;
	}
 
	public void setCategory(Category category) {
		this.category = category;
	}
	
	private Map<String, Object> request;
	private Map<String, Object> session;
	private Map<String, Object> application;
 
	@Override
	public void setApplication(Map<String, Object> application) {
		this.application = application;
	}
 
	@Override
	public void setSession(Map<String, Object> session) {
		this.session = session;
	}
 
	@Override
	public void setRequest(Map<String, Object> request) {
		this.request = request;
	}
}

还是上一节整合三大框架时的CategoryAction类,我们在里面加了一个query方法,在该方法中,我们通过向request域、session域和application域中存入查询的结果。第一种方法是直接使用ActionContext来实现,不需要实现任何接口,但是代码量较大;第二种方法通过实现RequestAware、SessionAware和ApplicationAware接口,实现该接口的三个抽象方法把request、session和application注入进来,然后赋给相应的成员变量中,这样就可以在query方法中向域中存放查询结果了。这代码量貌似比第一种方法更大……但是我们可以抽取,先往下看。
我们在index.jsp中新加一个查询连接来测试能否将查询结果显示出来:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>
  
  <body>
    <a href="${pageContext.request.contextPath }/category_update.action?category.id=2&category.type=gga&category.hot=false">访问update</a>
    <a href="category_save.action">访问save</a>
    <a href="category_query.action">查询所有类别</a><br/>
    <c:forEach items="${requestScope.categoryList }" var="category">
    	${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>
    
    <c:forEach items="${sessionScope.categoryList }" var="category">
    	${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>
    
    <c:forEach items="${applicationScope.categoryList }" var="category">
    	${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>
  </body>
</html>

3.2抽取BaseAction

刚刚提到了,第二种方法的代码量更大,但是我们可以抽取一个BaseAction,专门处理这些域相关的操作。

public class BaseAction extends ActionSupport implements RequestAware,SessionAware,ApplicationAware {
 
	protected Map<String, Object> request;
	protected Map<String, Object> session;
	protected Map<String, Object> application;
	
	@Override
	public void setApplication(Map<String, Object> application) {
		this.application = application;
	}
 
	@Override
	public void setSession(Map<String, Object> session) {
		this.session = session;
	}
 
	@Override
	public void setRequest(Map<String, Object> request) {
		this.request = request;
	}
}

然后我们自己的Action如果需要用到这些域对象来存储数据时,直接继承BaseAction即可,就能直接使用request、session和application对象了。所以修改后的CategoryAction如下:

public class CategoryAction extends BaseAction {
	
	private Category category;
<pre name="code" class="java">
        private CategoryService categoryService;
    
        public void setCategoryService(CategoryService categoryService) {
            this.categoryService = categoryService;
        }
        public String update() {
            System.out.println("----update----");
            categoryService.update(category);
             return "index"; 
         }
         public String save() {
         System.out.println("----save----");
         return "index"; 
         } 
         public String query() {
         request.put("categoryList", categoryService.query());
          session.put("categoryList", categoryService.query());
		 application.put("categoryList", categoryService.query());
	  	return "index"; 
	  } 
	  public Category getCategory() { 
	 	 return category;
	   } 
	   public void setCategory(Category category)
	    {
	   	 this.category = category;
	     }
	     }

后面所有要使用request、session和application域的Action,只要直接继承BaseAction即可,非常方便。

3.3获取参数ModelDriven

我们继续看上面的CategoryAction类,里面有个成员变量category,这是个POJO,定义这个变量并写好set和get方法是为了JSP页面可以通过url后面附带参数传进来,参数是category对象中的属性,比如id,type等,但是url中的参数必须写成category.id、category.type等。这样struts会自动将这写参数注入到category对象中,然后我们就可以直接使用这个category对象了,但是这样有点繁琐。我们可以使用ModelDriven来更方便的解决。

public class CategoryAction extends BaseAction implements ModelDriven<Category>{
	
	private Category category;
	
	//使用ModelDriven接口必须要实现getModel()方法,此方法会把返回的项压到栈顶
	@Override
	public Category getModel() {
		category = new Category();
		return category;
	}
<pre name="code" class="java">        private CategoryService categoryService;
    
        public void setCategoryService(CategoryService categoryService) {
            this.categoryService = categoryService;
        }
 
	public String update() {
		System.out.println("----update----");
		categoryService.update(category);
 		return "index";
 	}
	
	public String save() {
		System.out.println("----save----");
		return "index";
	}
	
	public String query() {
		request.put("categoryList", categoryService.query()); 
		session.put("categoryList", categoryService.query()); 
		application.put("categoryList", categoryService.query()); 
		return "index";
	}
 
}

这样我们在前台JSP页面就不用带category.id这种繁琐的参数了,看JSP页面中的ModelDriven部分:

 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>
  
  <body>
    <a href="${pageContext.request.contextPath }/category_update.action?category.id=2&category.type=gga&category.hot=false">访问update</a>
    <a href="category_save.action?id=1&type=haha&hot=true">测试ModelDriven</a>
    <a href="category_query.action">查询所有类别</a><br/>
    <c:forEach items="${requestScope.categoryList }" var="category">
    	${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>
    
    <c:forEach items="${sessionScope.categoryList }" var="category">
    	${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>
    
    <c:forEach items="${applicationScope.categoryList }" var="category">
    	${category.id } | ${category.type } | ${category.hot } <br/>
    </c:forEach>
  </body>
</html>

测试结果是可以获得catgory,并且将id,type和hot属性全部赋值好。我们可以看出,通过实现ModelDriven接口,我们可以很方便的在url中携带参数,Action中只需要实现getModel方法,new一个要使用的对象返回即可。到这里我们很容易想到,struts中肯定会有很多这种model需要获取,所以这一块我们也要抽取到BaseAction中去。

3.4抽取ModelDriven到BaseAction

首先我们在BaseAction中添加ModelDriven部分的代码,如下:

//因为有很多不同的model都需要使用ModelDriven,所以这里使用泛型
public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> {
 
	protected Map<String, Object> request;
	protected Map<String, Object> session;
	protected Map<String, Object> application;
	
	protected T model;
	
	@Override
	public void setApplication(Map<String, Object> application) {
		this.application = application;
	}
 
	@Override
	public void setSession(Map<String, Object> session) {
		this.session = session;
	}
 
	@Override
	public void setRequest(Map<String, Object> request) {
		this.request = request;
	}
 
	@Override
	public T getModel() { //这里通过解析传进来的T来new一个对应的instance
		ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
		Class clazz = (Class)type.getActualTypeArguments()[0];
		try {
			model = (T)clazz.newInstance();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}	
		return model;
	}
}

抽取完了后,CategoryAction中的代码会越来越少:

//继承BaseAction,并且加上泛型
public class CategoryAction extends BaseAction<Category> {
 
	private CategoryService categoryService;
	
	public void setCategoryService(CategoryService categoryService) {
		this.categoryService = categoryService;
	}
	
	public String update() {
		System.out.println("----update----");
		categoryService.update(model);//直接使用model
		return "index";
	}
	
	public String save() {
		System.out.println("----save----");
		System.out.println(model); //直接使用model
		return "index";
	}
	
	public String query() {	
		request.put("categoryList", categoryService.query()); 
		session.put("categoryList", categoryService.query()); 
		application.put("categoryList", categoryService.query()); 
		return "index";
	}
 
}

到这里,还有一个看着不爽的地方,就是categoryService这个成员变量,它一直存在在CategoryAction里,因为CategoryAction中有用到categoryService对象中的方法,所以必须得创建这个对象,并且有set方法才能注入进来。这就导致一个弊端:如果很多Action都需要使用categoryService的话,那就必须在它们的Action里创建这个对象和set方法,而且,如果一个Action中要使用好几个不同的service对象,那就得全部创建,这样就变得很冗杂。

3.5抽取service到BaseAction

针对上面的问题,我们将工程中所有的service对象都抽取到BaseAction中创建,这样其他Action继承BaseAction后,想用什么service就直接拿来用即可:

  //我将BaseAction中的内容归归类了
public class BaseAction<T> extends ActionSupport implements RequestAware,SessionAware,ApplicationAware,ModelDriven<T> {
 
	//service对象
	protected CategoryService categoryService;
	protected AccountService accountService;
	
	public void setCategoryService(CategoryService categoryService) {
		this.categoryService = categoryService;
	}
	public void setAccountService(AccountService accountService) {
		this.accountService = accountService;
	}
 
	//域对象
	protected Map<String, Object> request;
	protected Map<String, Object> session;
	protected Map<String, Object> application;
		
	@Override
	public void setApplication(Map<String, Object> application) {
		this.application = application;
	}
	@Override
	public void setSession(Map<String, Object> session) {
		this.session = session;
	}
	@Override
	public void setRequest(Map<String, Object> request) {
		this.request = request;
	}
	
	//ModelDriven
	protected T model;
	@Override
	public T getModel() {
		ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
		Class clazz = (Class)type.getActualTypeArguments()[0];
		try {
			model = (T)clazz.newInstance();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}	
		return model;
	}
}

这样CategoryAction中就更加清爽了:

 public class CategoryAction extends BaseAction<Category> {
	
	public String update() {
		System.out.println("----update----");
		categoryService.update(model);
		return "index";
	}
	
	public String save() {
		System.out.println("----save----");
		System.out.println(model);
		return "index";
	}
	
	public String query() {
		request.put("categoryList", categoryService.query()); 
		session.put("categoryList", categoryService.query()); 
		application.put("categoryList", categoryService.query()); 
		return "index";
	}
 
}

有人可能会问,BaseAction中注入了那么多service对象的话不会冗余么?这是不会的,因为就算不写在BaseAction中,Spring容器也是会创建这个对象的,这点没有关系,相反,service对象全放在BaseAction中更加便于其他Action的开发,而且BaseAction不需要配到struts.xml文件中,因为根本就没有哪个JSP会请求BaseAction,它只是让其他Action来继承用的。
还有一点别忘了:那就是修改在beans.xml中的配置:

<!-- 如果是prototype类型,默认是使用时创建,不是启动时自动创建 -->
<bean id="baseAction" class="cn.it.shop.action.BaseAction" scope="prototype">
	 <property name="categoryService" ref="categoryService"></property>
	 <property name="accountService" ref="accountService"></property>
</bean>
	 
<bean id="categoryAction" class="cn.it.shop.action.CategoryAction" scope="prototype" parent="baseAction"/>

新加一个baseAction的bean,将工程中所有service对象作为property配好,将原来的categoryAction中的property干掉。

以后我们如果要写新的xxxAction,直接继承BaseAction即可,如果xxxAction中有用到某个service,直接拿来用即可,只需要在beans.xml文件中加一个xxxAction对应的bean,在struts.xml文件中配置好跳转即可。

4将xml改成注解

我们可以看到,随着项目越写越大,beans.xml中的配置会越来越多,而且很多配置有冗余,为了更加便于开发,我们现在将xml的配置改成注解的形式,我们先看一下beans.xml中的配置:
在这里插入图片描述
这些是我们之前搭建环境以及抽取的时候写的bean,这些都需要转换成注解的形式,下面我们一块一块的换掉:首先替换service部分,这部分有三个:baseService、categoryService和accountService。替换如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后将beans.xml中的相应部分干掉即可。接下来修改ActIon部分,主要有baseAction、categoryAction和accountAction三个,替换如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后再干掉beans.xml中的Action部分的配置即可,最后在beans.xml文件中添加一个如下配置,就可以使用注解了。

 <context:component-scan base-package="cn.it.shop.."/>

有人可能会问,为什么service和action两个使用注解的时候不一样呢?service中使用的是@Service而action中使用的是@Controller呢?其实是一样的,只是为了区分它们是不同层的bean而已,便于阅读。

第六章 Spring4整合Hibernate4,Struts2

Spring+Struts2+Hibernate(SSH)整合框架是一种非常典型与实用的Web项目MVC架构,其中:
Spring主要通过控制反转(IOC)与依赖注入(DI)控制对象的创建与维护(概念详解可参看博客IOC与DI),与实现面向切片编程(AOP),核心配置文件为:applicationContext.xml。
Struts2是MVC的Controller控制器,主要控制请求与对应业务逻辑、视图的对应关系,核心配置文件为:struts.xml。
Hibernate是持久层框架,主要用于与数据库交互,它的优势在于它把实体类与数据库表建立了映射关系,在进行数据库操作时省去了繁琐的sql语句的构造过程与结果集的配置,使编程者可以以面向对象的思路来方便的进行编程,核心配置文件为:hibernate.cfg.xml与*.hbm.xml。

第一节 S2SH 整合所需 Jar 包

Struts2.3.16,Spring4.0.6,Hibernate4.3.3整合所需jar包
Struts2.3.16jar 包
在这里插入图片描述
Spring4.0.6jar 包
在这里插入图片描述
Hibernate4.3.5jar 包
在这里插入图片描述
新建项目S2SH
->导入
->驱动包mysql
->web.xml

<?xml version="1.0" encoding="UTF-8"?>  
<web-app version="3.0"   
    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">  
  <display-name></display-name>   
    
  <!-- 添加对spring的支持 -->  
  <context-param>  
    <param-name>contextConfigLocation</param-name>  
    <param-value>classpath:applicationContext.xml</param-value>  
  </context-param>  
    
    <!-- 定义Spring监听器,加载Spring  -->
    <listener>  
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>  
      
  <!-- 添加对struts2的支持 -->  
  <filter>  
    <filter-name>struts2</filter-name>  
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
  </filter>   
  
  <filter-mapping>  
    <filter-name>struts2</filter-name>  
    <url-pattern>/*</url-pattern>  
  </filter-mapping>  
  
  <!-- Session延迟加载到页面  --> 
  //在后台取数据时
   <filter>  
    <filter-name>openSessionInViewFilter</filter-name>  
    <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>  
    <init-param>  
      <param-name>singleSession</param-name>  
      <param-value>true</param-value>  
    </init-param>  
  </filter>  
    
   <filter-mapping>  
    <filter-name>openSessionInViewFilter</filter-name>  
    <url-pattern>*.action</url-pattern>  
  </filter-mapping>  
    
  <welcome-file-list>  
    <welcome-file>index.jsp</welcome-file>  
  </welcome-file-list>  
</web-app>  

->struts.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!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.action.extension" value="action" />  
   
</struts>

->hibernate.cfg.xml

<?xml version='1.0' encoding='UTF-8'?>  
<!DOCTYPE hibernate-configuration PUBLIC  
         "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">  
  
<hibernate-configuration>  
    <session-factory>  
		
		<!--方言-->
        <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>  

        <!-- 显示sql语句 -->  
        <property name="show_sql">true</property>  
        
		<!-- 自动更新 -->
        <property name="hbm2ddl.auto">update</property>
  
  
    </session-factory>  
</hibernate-configuration>

->applicationContext.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:p="http://www.springframework.org/schema/p"  
    xmlns:aop="http://www.springframework.org/schema/aop"   
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:jee="http://www.springframework.org/schema/jee"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="    
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd  
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">    
  
    <!-- 定义数据源 -->
    <bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName"
			value="com.mysql.jdbc.Driver">
		</property>
		<property name="url"
			value="jdbc:mysql://localhost:3306/test">
		</property>
		<property name="username" value="root"></property>
		<property name="password" value="123456"></property>
	</bean>
      
    <!-- session工厂 -->  
    <bean id="sessionFactory"  
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">  
        <property name="dataSource">  
            <ref bean="dataSource" />  
        </property>  
        <property name="configLocation" value="classpath:hibernate.cfg.xml"/>  
        <!-- 自动扫描注解方式配置的hibernate类文件 -->  
        <property name="packagesToScan">  
            <list>  
                <value>com.java1234.entity</value>  
            </list>  
        </property>  
    </bean>  
  
    <!-- 配置事务管理器 -->  
    <bean id="transactionManager"  
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">  
        <property name="sessionFactory" ref="sessionFactory" />  
    </bean>  
  
    <!-- 配置事务通知属性 -->  
    <tx:advice id="txAdvice" transaction-manager="transactionManager">  
        <!-- 定义事务传播属性 -->  
        <tx:attributes>  
            <tx:method name="insert*" propagation="REQUIRED" />  
            <tx:method name="update*" propagation="REQUIRED" />  
            <tx:method name="edit*" propagation="REQUIRED" />  
            <tx:method name="save*" propagation="REQUIRED" />  
            <tx:method name="add*" propagation="REQUIRED" />  
            <tx:method name="new*" propagation="REQUIRED" />  
            <tx:method name="set*" propagation="REQUIRED" />  
            <tx:method name="remove*" propagation="REQUIRED" />  
            <tx:method name="delete*" propagation="REQUIRED" />  
            <tx:method name="change*" propagation="REQUIRED" />  
            <tx:method name="get*" propagation="REQUIRED" read-only="true" />  
            <tx:method name="find*" propagation="REQUIRED" read-only="true" />  
            <tx:method name="load*" propagation="REQUIRED" read-only="true" />  
            <tx:method name="*" propagation="REQUIRED" read-only="true" />  
        </tx:attributes>  
    </tx:advice>  
      
   
    <!-- 配置事务切面 -->  
    <aop:config>  
        <aop:pointcut id="serviceOperation"  
            expression="execution(* com.java1234.service..*.*(..))" />  
        <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />  
    </aop:config>  
  
    <!-- 自动加载构建bean -->  
    //类全部用注解,所以这里就自动加载bean
    <context:component-scan base-package="com.java1234" />  
  
</beans>  

->com.java1234.dao
->com.java1234.dao.impl
hibernate
->com.java1234.service
->com.java1234.service.impl
->com.java1234.action
struts web层
->com.java1234.entity
->index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
S2SH !
</body>
</html>

->启动程序
http://localhost:8080/S2SH
页面显示: S2SH !

第二节 Spring4整合Hibernate4

Spring4接管Hibernate4所有的Bean实例,以及SessionFactory,事务管理器;
支持泛型注入;

第三节 Spring4整合Struts2

Spring4接管Struts2所有Bean实例;
总的来说Spring是个粘合剂,一统江湖.

第四节 S2SH实例测试

->BaseDao.java

package com.java1234.dao;

import java.io.Serializable;
import java.util.List;

/**
 * 基础数据库操作类
 * 
 * @author www.java1234.com
 * 
 */
public interface BaseDao<T> {

	/**
	 * 保存一个对象
	 * 
	 * @param o
	 * @return
	 */
	public Serializable save(T o);

	/**
	 * 删除一个对象
	 * 
	 * @param o
	 */
	public void delete(T o);

	/**
	 * 更新一个对象
	 * 
	 * @param o
	 */
	public void update(T o);

	/**
	 * 保存或更新对象
	 * 
	 * @param o
	 */
	public void saveOrUpdate(T o);

	/**
	 * 查询
	 * 
	 * @param hql
	 * @return
	 */
	public List<T> find(String hql);

	/**
	 * 查询集合
	 * 
	 * @param hql
	 * @param param
	 * @return
	 */
	public List<T> find(String hql, Object[] param);

	/**
	 * 查询集合
	 * 
	 * @param hql
	 * @param param
	 * @return
	 */
	public List<T> find(String hql, List<Object> param);

	/**
	 * 查询集合(带分页)
	 * 
	 * @param hql
	 * @param param
	 * @param page
	 *            查询第几页
	 * @param rows
	 *            每页显示几条记录
	 * @return
	 */
	public List<T> find(String hql, Object[] param, Integer page, Integer rows);

	/**
	 * 查询集合(带分页)
	 * 
	 * @param hql
	 * @param param
	 * @param page
	 * @param rows
	 * @return
	 */
	public List<T> find(String hql, List<Object> param, Integer page, Integer rows);

	/**
	 * 获得一个对象
	 * 
	 * @param c
	 *            对象类型
	 * @param id
	 * @return Object
	 */
	public T get(Class<T> c, Serializable id);

	/**
	 * 获得一个对象
	 * 
	 * @param hql
	 * @param param
	 * @return Object
	 */
	public T get(String hql, Object[] param);

	/**
	 * 获得一个对象
	 * 
	 * @param hql
	 * @param param
	 * @return
	 */
	public T get(String hql, List<Object> param);

	/**
	 * select count(*) from 类
	 * 
	 * @param hql
	 * @return
	 */
	public Long count(String hql);

	/**
	 * select count(*) from 类
	 * 
	 * @param hql
	 * @param param
	 * @return
	 */
	public Long count(String hql, Object[] param);

	/**
	 * select count(*) from 类
	 * 
	 * @param hql
	 * @param param
	 * @return
	 */
	public Long count(String hql, List<Object> param);

	/**
	 * 执行HQL语句
	 * 
	 * @param hql
	 * @return 响应数目
	 */
	public Integer executeHql(String hql);

	/**
	 * 执行HQL语句
	 * 
	 * @param hql
	 * @param param
	 * @return 响应数目
	 */
	public Integer executeHql(String hql, Object[] param);

	/**
	 * 执行HQL语句
	 * 
	 * @param hql
	 * @param param
	 * @return
	 */
	public Integer executeHql(String hql, List<Object> param);

}

->BaseDaOImpl.java

package com.java1234.dao.impl;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.java1234.dao.BaseDao;
//注解
@Repository("baseDao")
@SuppressWarnings("all")
public class BaseDaOImpl<T> implements BaseDao<T> {

	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	private Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	public Serializable save(T o) {
		return this.getCurrentSession().save(o);
	}

	public void delete(T o) {
		this.getCurrentSession().delete(o);
	}

	public void update(T o) {
		this.getCurrentSession().update(o);
	}

	public void saveOrUpdate(T o) {
		this.getCurrentSession().saveOrUpdate(o);
	}

	public List<T> find(String hql) {
		return this.getCurrentSession().createQuery(hql).list();
	}

	public List<T> find(String hql, Object[] param) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (param != null && param.length > 0) {
			for (int i = 0; i < param.length; i++) {
				q.setParameter(i, param[i]);
			}
		}
		return q.list();
	}

	public List<T> find(String hql, List<Object> param) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (param != null && param.size() > 0) {
			for (int i = 0; i < param.size(); i++) {
				q.setParameter(i, param.get(i));
			}
		}
		return q.list();
	}

	public List<T> find(String hql, Object[] param, Integer page, Integer rows) {
		if (page == null || page < 1) {
			page = 1;
		}
		if (rows == null || rows < 1) {
			rows = 10;
		}
		Query q = this.getCurrentSession().createQuery(hql);
		if (param != null && param.length > 0) {
			for (int i = 0; i < param.length; i++) {
				q.setParameter(i, param[i]);
			}
		}
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}

	public List<T> find(String hql, List<Object> param, Integer page, Integer rows) {
		if (page == null || page < 1) {
			page = 1;
		}
		if (rows == null || rows < 1) {
			rows = 10;
		}
		Query q = this.getCurrentSession().createQuery(hql);
		if (param != null && param.size() > 0) {
			for (int i = 0; i < param.size(); i++) {
				q.setParameter(i, param.get(i));
			}
		}
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}

	public T get(Class<T> c, Serializable id) {
		return (T) this.getCurrentSession().get(c, id);
	}

	public T get(String hql, Object[] param) {
		List<T> l = this.find(hql, param);
		if (l != null && l.size() > 0) {
			return l.get(0);
		} else {
			return null;
		}
	}

	public T get(String hql, List<Object> param) {
		List<T> l = this.find(hql, param);
		if (l != null && l.size() > 0) {
			return l.get(0);
		} else {
			return null;
		}
	}

	public Long count(String hql) {
		return (Long) this.getCurrentSession().createQuery(hql).uniqueResult();
	}

	public Long count(String hql, Object[] param) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (param != null && param.length > 0) {
			for (int i = 0; i < param.length; i++) {
				q.setParameter(i, param[i]);
			}
		}
		return (Long) q.uniqueResult();
	}

	public Long count(String hql, List<Object> param) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (param != null && param.size() > 0) {
			for (int i = 0; i < param.size(); i++) {
				q.setParameter(i, param.get(i));
			}
		}
		return (Long) q.uniqueResult();
	}

	public Integer executeHql(String hql) {
		return this.getCurrentSession().createQuery(hql).executeUpdate();
	}

	public Integer executeHql(String hql, Object[] param) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (param != null && param.length > 0) {
			for (int i = 0; i < param.length; i++) {
				q.setParameter(i, param[i]);
			}
		}
		return q.executeUpdate();
	}

	public Integer executeHql(String hql, List<Object> param) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (param != null && param.size() > 0) {
			for (int i = 0; i < param.size(); i++) {
				q.setParameter(i, param.get(i));
			}
		}
		return q.executeUpdate();
	}

}

->User.java

package com.java1234.entity;


import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="t_user")
public class User {

	private Integer id;
	private String userName;
	private String password;

	
	@Id
	@GenericGenerator(name = "generator", strategy = "native")
	@GeneratedValue(generator = "generator")
	@Column(name = "id", length=11)
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	@Column(name = "userName", length = 20)
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	@Column(name = "password", length = 20)
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
}

->UserService.java

package com.java1234.service;

import java.util.List;

import com.java1234.entity.User;

public interface UserService {

	public void saveUser(User user);
	
	public void updateUser(User user);
	
	public User findUserById(int id);
	
	public void deleteUser(User user);
	
	public List<User> findAllList();
	
	public User findUserByNameAndPassword(User user);
}

->UserServiceImpl.java

package com.java1234.service.impl;

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import com.java1234.dao.BaseDao;
import com.java1234.entity.User;
import com.java1234.service.UserService;

@Service("userService")
public class UserServiceImpl implements UserService{
	//注入不需要写set了,这里只需要注解一下就可以得到了,泛型注入
	@Resource
	private BaseDao<User> baseDao;
	
	@Override
	public void saveUser(User user) {
		// TODO Auto-generated method stub
		baseDao.save(user);
	}

	@Override
	public void updateUser(User user) {
		// TODO Auto-generated method stub
		baseDao.update(user);
	}

	@Override
	public User findUserById(int id) {
		return baseDao.get(User.class, id);
	}

	@Override
	public void deleteUser(User user) {
		baseDao.delete(user);
	}

	@Override
	public List<User> findAllList() {
		return baseDao.find("from User");
	}

	@Override
	public User findUserByNameAndPassword(User user) {
		return baseDao.get("from User u where u.userName=? and u.password=?", new Object[]{user.getUserName(),user.getPassword()});
	}

}

->UserAction.java struts

package com.java1234.action;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.struts2.interceptor.ServletRequestAware;
import org.springframework.stereotype.Controller;

import com.java1234.entity.User;
import com.java1234.service.UserService;
import com.opensymphony.xwork2.ActionSupport;

@Controller
public class UserAction extends ActionSupport implements ServletRequestAware{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private HttpServletRequest request;
    //注入UserService 
	@Resource
	private UserService userService;
	
	private User user;
	private String error;
	
	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}

	public String getError() {
		return error;
	}

	public void setError(String error) {
		this.error = error;
	}

	public String login()throws Exception{
		HttpSession session=request.getSession();
		User currentUser=userService.findUserByNameAndPassword(user);
		if(currentUser!=null){
			session.setAttribute("currentUser", currentUser);
			return SUCCESS;
		}else{
			error="用后名或者密码错误!";
			return ERROR;
		}
	}

	@Override
	public void setServletRequest(HttpServletRequest request) {
		// TODO Auto-generated method stub
		this.request=request;
	}

}

->struts.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!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.action.extension" value="action" />  
      
    <package name="s2sh" namespace="/user" extends="struts-default">
    	//	匹配
    	<action name="user_*" method="{1}" class="com.java1234.action.UserAction">
    		<result name="success">/success.jsp</result>
    		<result name="error">/index.jsp</result>
    	</action>
    </package>
      
</struts>

->index.jsp

<%@ 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>Insert title here</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/user/user_login.action" method="post">
	userName:<input type="text" name="user.userName" value="${user.userName }"/><br/>
	password:<input type="password" name="user.password" value="${user.password }"><br/>
	<input type="submit" value="login"/><font color="red">${error }</font>
</form>
</body>
</html>

->success.jsp

<%@ 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>Insert title here</title>
</head>
<body>
欢迎:${currentUser.userName }
</body>
</html>

->执行程序
http://localhost:8080/S2SH
在页面中输入名字和密码
若和数据库中的一致,跳入成功页面。
若不一致,提示错误信息。

猜你喜欢

转载自blog.csdn.net/AthlenaA/article/details/82982139