SSH 框架 —— Struts2 框架、Spring 框架、Hibernate 框架三大框架整合

一、SSH 框架整合

1、SSH 整合概述

SSH框架整合思路:使用SSH三大框架時

  • Struts2 是作用於控制層負責對請求進行統一處理和分發,Struts2 會根據請求地址去創建對應的Action的對象,並執行相應的方法。
  • Hibernate 作爲ORM框架致力於簡化數據層操作。
  • 所謂的SSH三大框架的整合其實就是通過 Spring 的IOC來創建 Struts2 中的 Action 對象以及Hibernate 中的 SessionFactory 對象。

二、SSH 框架整合步驟

1、在 WEB 項目中加載 Spring 框架

Spring提供了一個監聽器 ContextLoaderListener,該監聽器實現了ServletContextListener 接口,在服務器啓動時,該監聽器就會執行,執行該監聽器的目的是讀取applicationContext.xml 文件,並根據文件的配置創建項目中所需要的對象並完成依賴注入。
(1)導入spring框架中所需的jar包
在這裏插入圖片描述
(2)在 web.xml 中加載 spring 提供的監聽器

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

(3)ContextLoaderListener 在執行時會默認去讀取WEB-INF下的 applicationContext.xml文件,如果想自定義配置文件名稱需要使用<context-param>標籤配置上下文參數,配置的上下文參數在監聽器中可以獲取。

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:application.xml</param-value>
</context-param>

參數名稱contextConfigLocation,參數值classpath:文件名稱,表示該文件處於src目錄之下。

(4)根據第三步的配置在適當的位置新建applicationContext.xml文件,在 xml 文件中引入beansaopcontexttx的命名空間。

<?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-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/aop
		http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/tx 
		http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
</beans>	

2、使用 Spring 框架整合 Struts2

(1)導入 Struts2 所需的jar包,本次要導入到項目中的jar包包括 Struts2 本身所需要的包、JSON的相關包、Struts2-Spring 的整合插件包。
在這裏插入圖片描述
(2)在 web.xml 中加載 Struts2 的核心控制器

<!-- 加載Struts2的核心控制器,過濾器 -->
<filter>
    <filter-name>filter</filter-name>
    <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

(3)在 src 下新建struts.xml文件,並配置 action 標籤,和以前單獨使用 Struts2 時相比有兩點不同,首先 Action 類以前是 Struts2 框架負責創建,使用 Spring 以後需要將該對象的創建工作交給 Spring 來負責,所以需要在Action類上添加@Controller註解或者在applicationContext.xml中使用<bean>標籤來創建Action對象。
註解方式:

@Controller
@Scope("prototype")
public class InsertAction extends ActionSupport{
	//新增方法
	public String insert() {
		System.out.println("執行新增操作");
		return Action.SUCCESS;
	}
}
  • @Scope("prototype")是設置 Spring 創建 Action 對象時採用非單例模式。

使用註解方式需要在applicationContext.xml文件中配置掃包地址:

<!-- 配置Spring掃描包的地址 -->
<context:component-scan base-package="com.ssh"></context:component-scan>

標籤配置方式:

<bean id="insertAction" class="com.ssh.action.InsertAction" scope="prototype"></bean>

第二點不同在於 struts.xml 文件中,單獨使用 Struts2 時,action 標籤中的 class 屬性表達的是處理該請求時需要執行的類的名稱,Struts2 會創建該類型的對象,但是一旦和 Spring 整合之後該類對象已經被 Spring 創建,這時在 class 中直接寫 Spring 容器中對象的 bean id 即可。

<struts>
    <package name="default" namespace="/" extends="struts-default">
        <action name="insert" class="insertAction" method="insert">
            <result name="success">success.jsp</result>
        </action>
    </package>
</struts>

除此之外使用 Struts2 的過程和以前單獨使用沒有什麼區別。

3、使用 Spring 框架整合 Hibernate

Spring 整合 Struts2 完畢以後就可以按照以前 Spring 中註解的方式來實現對象創建和依賴注入工作了。
由於在數據層實現類中需要使用 Session 對象來執行 Hibernate 的增刪改查,這時我們可以使用 Spring 的IOC來幫助我們創建 Session 對象並將 Session 對象注入到數據層對象中。
(1)導入 Hibernate 的必要包、C3P0的包、數據庫驅動包,如果有使用 Hibernate 二級緩存的需求還需要導入第三方的二級緩存依賴包。
在這裏插入圖片描述
(2)新建測試實體類 Products類,並添加 Hibernate 的必要註解。

@Entity
public class Products {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int p_id;
	private String p_name;
	private double p_price;
	private int p_count;
	private String p_class;
	private String p_attribute;
	private int p_typeid;
	//省略get、set方法
	@Override
	public String toString() {
		return "Products [p_id=" + p_id + ", p_name=" + p_name + ", p_price=" + p_price + ", p_count=" + p_count
				+ ", p_class=" + p_class + ", p_attribute=" + p_attribute + ", p_typeid=" + p_typeid + "]";
	}
}

(3)創建 Session 對象依賴於 SessionFactory 對象,創建 SessionFactory 對象依賴於數據源對象,本案例中採用C3P0連接池作爲數據源,如果要換其他的連接池,只需要更改數據源的配置即可,其他模塊保持不變。
數據源配置:
在配置數據源時需要用到很多的常量例如數據庫連接信息,連接池的基本信息等等,這些信息最好是保存在外部的資源文件中,便於統一的管理。
① jdbc.properties 文件

#jdbc連接信息
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf-8
jdbc.user=root
jdbc.password=admin
#c3p0各項屬性
c3p0.minPoolSize=10
c3p0.maxPoolSize=50
c3p0.initialPoolSize=20
c3p0.maxIdleTime=60
c3p0.acquireIncrement=1
c3p0.checkoutTimeout=6000
c3p0.idleConnectionTestPeriod=600

② 資源文件準備好以後,就可以在applicationContext中引入資源文件,便於後續的使用,在前面的學習過程中我們使用了一個資源文件加載器來加載資源文件。

<!-- 配置加載器加載資源文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties"></property>
</bean>

Spring-Context 包提供了上述加載器的簡寫:

<context:property-placeholder location="classpath:jdbc.properties"/>

③ 使用bean標籤配置數據源

<bean id="pool" class="com.mchange.v2.c3p0.ComboPooledDataSource">
	<!-- 提供數據庫連接信息和連接池的常用信息 -->
	<!-- 驅動類 -->
	<property name="driverClass" value="${jdbc.driverClass}" />
	<!-- 連接數據庫的地址 -->
	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
	<!-- 連接數據庫的用戶名 -->
	<property name="user" value="${jdbc.user}" />
	<!-- 連接數據庫的密碼 -->
	<property name="password" value="${jdbc.password}" />
	<!-- c3p0類 -->
	<!-- 最大連接數 -->
	<property name="maxPoolSize" value="${c3p0.maxPoolSize}"></property>
	<!-- 最小連接數 -->
	<property name="minPoolSize" value="${c3p0.minPoolSize}"></property>
	<!-- 初始化連接數 -->
	<property name="initialPoolSize" value="${c3p0.initialPoolSize}"></property>
	<!-- 自增長連接數 -->
	<property name="acquireIncrement" value="${c3p0.acquireIncrement}"></property>
	<!-- 連接對象空閒時間  超出10分鐘銷燬 -->
	<property name="maxIdleTime" value="${c3p0.maxIdleTime}"></property>
	<!-- 連接數據庫的等待時間 超出等待時間 拋出異常 -->
	<property name="checkoutTimeout" value="${c3p0.checkoutTimeout}"></property>
	<!-- 檢查連接的間隔時間 -->
	<property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"></property>
</bean>

(4)使用 bean 標籤配置 SessionFactory 對象

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
	<!-- 注入連接池,包含了數據庫用戶名,密碼等等信息 -->
	<property  name="dataSource" ref="pool"/>
	<!-- 配置Hibernate的其他的屬性 -->
	<property  name="hibernateProperties">
	<props>
		<prop key="hibernate.dialect">org.hibernate.dialect.MySQL55Dialect</prop>
		<prop key="hibernate.show_sql">true</prop>
		<prop key="hibernate.format_sql">true</prop>
		<prop key="hibernate.hbm2ddl.auto">update</prop>
		<prop key="hibernate.current_session_context_class">
			org.springframework.orm.hibernate5.SpringSessionContext</prop>
	</props>
	</property>
	<!--配置實體類所在包名  -->
	<property name="packagesToScan" value="com.ssh.entity"/>
</bean>

配置完sessionFactory之後就可以將該對象注入到數據層對象中以便於獲取Session對象。

4、使用 SpringAOP 實現聲明式事務管理

<!--事務管理器 通知-->
<bean id="txManger" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory"></property>
</bean>

<!-- 事務通知: 攔截到指定的方法後進行怎樣的事務管理 -->
<!-- 注意: 需要引入事務管理器類(攔截到指定的方法,且指定對指定的方法如何管理事務後,再應用事務管理器事務控制代碼!) -->
<!-- save* delete* update* 表示對所有的以save、update、delete開頭的方法採用讀寫事務! -->
<!-- * 最後一個* 表示上面都不滿足情況的其他方法,採用的事務只讀的! -->
<tx:advice id="txAdvice" transaction-manager="txManger">
	<tx:attributes>
		<tx:method name="save*" read-only="false" propagation="REQUIRED"/>
		<tx:method name="delete*" read-only="false" />
		<tx:method name="update*" read-only="false" />
		<tx:method name="*" read-only="true" />
	</tx:attributes>
</tx:advice>
<!-- Aop 配置 = 切入點表達式(指定給哪些對象生成代理對象) + 應用上面的事務通知 -->
<!-- 需要引入事務通知控制,以及通過切入點表達式指定攔截的方法 -->
<aop:config>
	<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.woniu.service.impl.*.*(..))" />
</aop:config>

(1)在使用聲明式事務以後,在業務層中不需要在手動的處理事務,也不能在業務層中進行異常處理,如果在業務層處理了異常,異常增強處理會以爲捕獲不到異常而失效,事務也就不會回滾,在選擇 openSession 和 getCurrentSession 時,如果執行增刪改使用 getCurrentSession,執行查詢使用 openSession,注意 openSession 獲取到的是全新的 Session 對象,並且開啓了自動提交。getCurrentSession 會從 Session 容器中獲取已將創建好的 Session 對象,該 Session 對象在一個線程 中都是唯一的,並且關閉了自動提交。

(2)<tx:method >標籤中的 propagation 屬性用於定義事務傳播機制,主要用在業務層方法相互調用時的事務處理方式。默認值爲REQUIRED,該屬性的可選值如下:

  • REQUIRED:支持當前事務,當前沒有事務,就新建一個事務。這是最常見的選擇。
  • SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行。
  • MANDATORY:支持當前事務,如果當前沒有事務,就拋出異常。
  • REQUIRED_NEW:新建事務,如果當前存在事務,把當前事務掛起。
  • NOT_SUPPORTED:以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
  • NEVER:以非事務方式執行,如果當前存在事務,則拋出異常。
  • NESTED:如果當前事務存在,則在嵌套事務中執行。如果當前沒有事務,就新建一個事務。

(3)三種讀寫事務的詳細說明:

  • REQUIRED,場景:業務A方法調用B方法。A已經是 REQUIRED 事務,B採用 REQUIRED
    在該場景中由於兩個業務方法使用了同一個事務,所以任何一個業務方法出現異常都會回滾兩個業務中的數據操作。
  • PROPAGATION_REQUIRED_NEW,場景:業務A方法調用B方法。A已經是REQUIRED事務,B採用PROPAGATION_REQUIRED_NEW。
    在該場景中業務A方法和業務B方法都擁有獨立的事務,所以無論其中哪個數據方法出現異常都只會回滾自己的數據操作。
  • PROPAGATION_NESTED,場景:業務A方法調用B方法。A已經是REQUIRED事務,B採用PROPAGATION_NESTED。
    當B方法事務提交完成時是把B方法之前包括A的操作進行提交。當B回滾時只是B自己的數據操作回滾不會影響到A的數據操作。

(4)在添加好聲明式事務管理以後,在數據層執行增刪改時要使用 currentSession 方法來獲取 session 對象,同時一定要注意業務層的方法命名規範。

5、使用 OpenSessionInView 延遲加載後 session 的關閉問題

(1)Hibernate 爲了降低數據庫壓力,在設計時對於數據的查詢採用了延遲查詢的方式,也就是當數據被真正使用時,Hibernate 纔會去執行查詢,但是在WEB項目中數據真正使用往往發生在JSP解析階段或者是格式化JSON的階段。在這時 session 往往已經關閉了,這時程序就會出現異常。
這時我們可以採用 Spring 提供的 OpenSessionInView 的方式將 session 的關閉工作放在執行完控制層代碼以後。Spring 提供了一個過濾器來實現該功能。

<filter>      
	<filter-name>hibernateFilter</filter-name>      
	<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>      
	<!-- singleSession默認爲true,若設爲false則等於沒用OpenSessionInView -->      
	<init-param>      
		<param-name>singleSession</param-name>      
		<param-value>true</param-value>      
	</init-param>      
</filter> 
<filter-mapping>      
	<filter-name>hibernateFilter</filter-name>      
	<url-pattern>*.action</url-pattern>      
</filter-mapping> 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章