day35_Spring學習筆記_03

一、事務管理

1.1、回顧事務

  • 事務:一組業務操作ABCD,要麼全部成功,要麼全部不成功。
  • 特性:ACID 原子性:整體 一致性:完成 隔離性:併發 持久性:結果
  • 隔離問題: 髒讀:一個事務讀到另一個事務沒有提交的數據。讀到的是假的數據。 不可重複讀:一個事務讀到另一個事務已提交的數據(update)。每次讀到的結果不一樣。 虛讀(幻讀):一個事務讀到另一個事務已提交的數據(insert)。讀到的數據變多了。
  • 隔離級別: read uncommitted:讀未提交。存在3個問題。 read committed:讀已提交。解決髒讀,存在2個問題。 repeatable read:可重複讀。解決髒讀、不可重複讀,存在1個問題。 serializable:串行化。都解決,單事務。
  • mysql 事務操作(簡單)
ABCD 是一個事務,有4個業務
Connection conn = null;
try {
  // 1、獲得連接
  conn = ...;
  // 2、開啓事務
  conn.setAutoCommit(false);
  A
  B
  C
  D
  // 3、提交事務
  conn.commit();
} catch() {
  // 4、回滾事務
  conn.rollback();
}
  • mysql 事務操作 => Savepoint
需求:AB(必須業務),CD(可選業務) 
Connection conn = null;
Savepoint savepoint = null;  // 定義保存點,記錄操作的當前位置,之後可以回滾到指定的位置。(可以回滾一部分)
try {
  // 1、獲得連接
  conn = ...;
  // 2、開啓事務
  conn.setAutoCommit(false);
  A
  B
  savepoint = conn.setSavepoint();
  C
  D
  // 3、提交事務
  conn.commit();
} catch() {
  if (savepoint != null) {   // 說明CD異常
     // 回滾到CD之前
     conn.rollback(savepoint);
     // 提交AB
     conn.commit();
  } else {   // 說明AB異常
     // 回滾AB之前
     conn.rollback();
  }
}

1.2、事務管理介紹

1.2.1、導入jar包

transaction --> tx

1.2.2、三個頂級接口

  • PlatformTransactionManager 平臺事務管理器,spring要管理事務,必須使用事務管理器 進行事務配置時,必須配置事務管理器
  • TransactionDefinition 事務詳情(事務定義、事務屬性),spring用於確定事務的具體詳情 例如:隔離級別、是否只讀、超時時間 等等。 進行事務配置時,必須配置事務詳情。spring將配置項封裝到該對象實例。
  • TransactionStatus 事務狀態,spring用於記錄當前事務的運行狀態。 例如:是否有保存點,事務是否完成。 spring底層根據不同的運行狀態進行相應操作。跟我們沒有關係,我們只需要知道即可。

1.2.3、PlatformTransactionManager 事務管理器

  • 導入jar包:我們需要的是平臺事務管理器的實現類
  • 常見的事務管理器(必須記住) DataSourceTransactionManager jdbc開發時使用的事務管理器,採用JdbcTemplate HibernateTransactionManager hibernate開發時使用的事務管理器,整合hibernate時使用
  • api詳解 TransactionStatus getTransaction(TransactionDefinition definition) “事務管理器”通過“事務詳情”,獲得“事務狀態”,從而管理事務。 void commit(TransactionStatus status) 根據狀態提交 void rollback(TransactionStatus status) 根據狀態回滾

1.2.4、TransactionStatus 事務狀態(瞭解)

1.2.5、TransactionDefinition 事務詳情

傳播行爲:在兩個業務之間如何共享事務。每個值的詳解如下:

  • PROPAGATION_REQUIRED , required , 必須【默認值】
    • 支持當前事務,A如果有事務,B將使用該事務。
    • 如果A沒有事務,B將創建一個新的事務。即B永遠處在事務中。
  • PROPAGATION_SUPPORTS , supports , 支持
    • 支持當前事務,A如果有事務,B將使用該事務。
    • 如果A沒有事務,B將以非事務執行。
  • PROPAGATION_MANDATORY , mandatory , 強制
    • 支持當前事務,A如果有事務,B將使用該事務。
    • 如果A沒有事務,B將拋出異常。
  • PROPAGATION_REQUIRES_NEW , requires_new , 必須新的
    • 如果A有事務,將A的事務掛起,B將創建一個新的事務。
    • 如果A沒有事務,B將創建一個新的事務。
  • PROPAGATION_NOT_SUPPORTED , not_supported , 不支持
    • 如果A有事務,將A的事務掛起,B將以非事務執行。
    • 如果A沒有事務,B將以非事務執行。
  • PROPAGATION_NEVER , never , 從不
    • 如果A有事務,B將拋出異常。
    • 如果A沒有事務,B將以非事務執行。
  • PROPAGATION_NESTED , nested , 嵌套
    • A和B底層採用保存點機制,形成嵌套事務。

掌握:PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEWPROPAGATION_NESTED

1.3、事務案例:轉賬

1.3.1、搭建環境

1.3.1.1、創建表

CREATE DATABASE day35;
USE day35;

CREATE TABLE account(
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50),
  money INT
);

INSERT INTO account(username, money) VALUES('jack', '10000');
INSERT INTO account(username, money) VALUES('rose', '10000');

1.3.1.2、導入jar包

  • 核心:4 + 1
  • aop:4 (aop 聯盟、spring aop、aspectj 規範、spring aspect)
  • 數據庫:2(jdbc/tx)
  • 驅動:mysql
  • 連接池:c3p0

1.3.1.3、dao層

AccountDao.java

package com.itheima.dao;

public interface AccountDao {
    /**
     * 匯款
     * 
     * @param outer
     * @param money
     */
    public void out(String outer, Integer money);

    /**
     * 收款
     * 
     * @param inner
     * @param money
     */
    public void in(String inner, Integer money);
}

AccountDaoImpl.java

package com.itheima.dao.Impl;

import org.springframework.jdbc.core.support.JdbcDaoSupport;

import com.itheima.dao.AccountDao;

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    @Override
    public void out(String outer, Integer money) {
        this.getJdbcTemplate().update("update account set money=money-? where username=?", money, outer);
    }

    @Override
    public void in(String inner, Integer money) {
        this.getJdbcTemplate().update("update account set money=money+? where username=?", money, inner);
    }
}

1.3.1.4、service層 AccountService.java

package com.itheima.service;

public interface AccountService {
    /**
     * 轉賬
     * 
     * @param outer
     * @param inner
     * @param money
     */
    public void transfer(String outer, String inner, Integer money);
}

AccountServiceImpl.java

package com.itheima.service.impl;

import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;

public class AccountServiceImpl implements AccountService{

    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void transfer(String outer, String inner, Integer money) {
        accountDao.out(outer, money);

        // 斷電
        // int i = 1/0; // 斷電會出現事務問題

        accountDao.in(inner, money);
    }
}

1.3.1.5、spring配置 爲了更加接近實際開發,配置文件的名稱改爲 applicationContext.xml,配置文件的位置放在 src目錄下。 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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/aop 
                              http://www.springframework.org/schema/aop/spring-aop.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context.xsd">                      
    <!-- datasource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/day35"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- dao -->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

</beans>

1.3.1.6、測試 TestApp.java

package com.itheima;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.itheima.service.AccountService;

public class TestApp {
    @Test
    public void demo01() {
        String xmlPath = "applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        AccountService accountService = (AccountService) applicationContext.getBean("accountService");

        accountService.transfer("jack", "rose", 100);
    }
}

1.3.2、手動管理事務(瞭解)

  • spring底層使用 TransactionTemplate事務模板 來進行操作的。
  • 如何操作呢?步驟如下:
    • 1、service層需要獲得 TransactionTemplate事務模板
    • 2、spring就需要配置模板,並注入給service層
    • 3、配置模板又需要注入事務管理器
    • 4、配置事務管理器 DataSourceTransactionManager,又需要注入DataSource

1.3.2.1、修改service層代碼 AccountServiceImpl.java

package com.itheima.service.impl;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;

public class AccountServiceImpl implements AccountService{

    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    // 需要Spring注入模板
    private TransactionTemplate transactionTemplate;
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    @Override
    public void transfer(final String outer, final String inner, final Integer money) {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {

            @Override
            protected void doInTransactionWithoutResult(TransactionStatus arg0) {
                accountDao.out(outer, money);
                // 斷電
                int i = 1/0; // 斷電會出現事務問題
                accountDao.in(inner, money);
            }
        });
    }
}

1.3.2.2、修改spring配置 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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/aop 
                              http://www.springframework.org/schema/aop/spring-aop.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context.xsd">                      
    <!-- datasource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/day35"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- dao -->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
        <property name="transactionTemplate" ref="transactionTemplate"></property>
    </bean>

    <!-- 創建模板 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="txManager"></property>
    </bean>
    <!-- 配置事務管理器,注意:事務管理器需要事務,而事務從  連接Connection 獲得,而  連接Connection 從  連接池DataSource 處獲得 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

</beans>

1.3.3、使用工廠bean 生成代理:半自動

  • spring提供了管理事務的代理工廠bean:TransactionProxyFactoryBean,操作步驟如下:
    • 1、getBean() 獲得代理對象
    • 2、在spring中配置一個代理

1.3.3.1、spring配置 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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/aop 
                              http://www.springframework.org/schema/aop/spring-aop.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context.xsd">                      
    <!-- datasource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/day35"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- dao -->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!-- service 的代理對象 
        4.1 proxyInterfaces 接口 
        4.2 target 目標類
        4.3 transactionManager 事務管理器
        4.4 transactionAttributes 事務屬性(事務詳情)
            prop.key :確定哪些方法使用當前事務配置
            prop.text :用於配置事務詳情
                格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
                        傳播行爲        隔離級別        是否只讀    發生異常後仍然異常回滾事務   發生異常後仍然提交事務
                例如:
                    <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>  默認傳播行爲,和默認隔離級別
                    <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> 只讀
                    <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.ArithmeticException</prop>   發生異常後仍然提交事務
    -->
    <bean id="proxyAccountService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="preInterceptors" value="com.itheima.service.AccountService"></property>
        <property name="target" ref="accountService"></property>
        <property name="transactionManager" ref="txManager"></property>
        <property name="transactionAttributes">
            <props>
                <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>  <!-- 默認傳播行爲,和默認隔離級別 -->
            </props>
        </property>
    </bean>

    <!-- 事務管理器  -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

</beans>

1.3.3.2、測試 TestApp.java

package com.itheima;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.itheima.service.AccountService;

public class TestApp {
    @Test
    public void demo01() {
        String xmlPath = "applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        AccountService accountService = (AccountService) applicationContext.getBean("proxyAccountService");

        accountService.transfer("jack", "rose", 100);
    }
}

1.3.4、AOP 配置基於xml:全自動【掌握】

  • 在spring xml 配置了aop,就會自動生成代理,之後就可以進行事務的管理,操作步驟如下:
    • 1、配置管理器
    • 2、配置事務詳情
    • 3、配置aop

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: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/aop 
                              http://www.springframework.org/schema/aop/spring-aop.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context.xsd
                              http://www.springframework.org/schema/tx 
                              http://www.springframework.org/schema/tx/spring-tx.xsd">                        
    <!-- datasource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/day35"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- dao -->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!-- 事務管理 -->
    <!-- 4.1 事務管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 4.2 事務詳情(事務通知),在 aop編程  篩選的基礎上,對ABC三個確定使用什麼樣的事務。例如:AC讀寫、B只讀 等等。
        <tx:attributes> 用於配置事務詳情(屬性屬性)
            <tx:method name=""/> 詳情具體配置
                propagation 傳播行爲 , REQUIRED:必須; REQUIRES_NEW:必須是新的
                isolation 隔離級別
    -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>

    <!-- 4.3 AOP編程,例如:我們的目標類有ABCD(4個連接點),使用切入點表達式,確定需要增強的連接點,從而獲得切入點:ABC -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/>
    </aop:config>   

</beans>

1.3.5、AOP 配置基於註解:全自動【掌握】

  • 操作步驟如下:
    • 1、配置事務管理器,並將事務管理器交予spring管理
    • 2、在目標類或目標方法添加註解即可 @Transactional

1.3.5.1、spring配置 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: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/aop 
                              http://www.springframework.org/schema/aop/spring-aop.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context.xsd
                              http://www.springframework.org/schema/tx 
                              http://www.springframework.org/schema/tx/spring-tx.xsd">                        
    <!-- datasource -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/day35"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- dao -->
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- service -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!-- 事務管理 -->
    <!-- 4.1 事務管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 4.2  將事務管理器交予spring管理 
        * transaction-manager 配置事務管理器
        * proxy-target-class
            true : 底層強制使用  cglib代理
            false :默認的值爲false
    -->
    <tx:annotation-driven transaction-manager="txManager" proxy-target-class="false"/>

</beans>

1.3.5.2、service 層 AccountServiceImpl.java

package com.itheima.service.impl;

import org.springframework.transaction.annotation.Transactional;

import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;

@Transactional
public class AccountServiceImpl implements AccountService{

    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void transfer(String outer, String inner, Integer money) {
        accountDao.out(outer, money);

        // 斷電
        // int i = 1/0; // 斷電會出現事務問題

        accountDao.in(inner, money);
    }
}

1.3.5.3、事務詳情配置

AccountServiceImpl.java

package com.itheima.service.impl;

import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;

@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService{

    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void transfer(String outer, String inner, Integer money) {
        accountDao.out(outer, money);

        // 斷電
        // int i = 1/0; // 斷電會出現事務問題

        accountDao.in(inner, money);
    }
}

二、整合 Junit

步驟如下:

  • 導入jar包
    • 基本 :4 + 1
    • 測試:spring-test…jar
  • 1、讓Junit去通知spring加載配置文件
  • 2、讓spring容器自動進行注入

TestApp.java

package com.itheima;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.itheima.service.AccountService;

// 1、讓Junit去通知spring加載配置文件
// 2、讓spring容器自動進行注入

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class TestApp {

    @Autowired // 這是與junit整合,不需要在spring xml中進行配置掃描
    private AccountService accountService;

    @Test
    public void demo01() {
        // String xmlPath = "applicationContext.xml";
        // ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        // AccountService accountService = (AccountService) applicationContext.getBean("accountService");

        accountService.transfer("jack", "rose", 100);
    }
}

三、整合 web

步驟如下:

0、導入jar包
    spring-web-3.2.0.RELEASE.jar

1、tomcat啓動時就加載配置文件的方案:
    方案1:servlet --> init(ServletConfig) --> <load-on-startup>2
    方案2:filter --> init(FilterConfig) --> web.xml註冊過濾器後,就會自動調用初始化
    方案3:listener --> ServletContextListener --> servletContext對象監聽
    方案4:spring提供了一個監聽器 ContextLoaderListener  --> web.xml (<listener><listener-class>...)
            如果只配置了監聽器,則默認加載xml文件的位置爲:/WEB-INF/applicationContext.xml

2、確定配置文件位置,通過系統初始化參數
    ServletContext的初始化參數配,在web.xml文件中進行配置:
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
3、從servletContext作用域中獲得spring容器(瞭解即可,很少用)

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1">
    <display-name>day35_04_Spring_tx</display-name>

  <!-- 確定配置文件位置 -->
  <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <!-- 配置spring 監聽器,用於加載xml配置文件 -->
  <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>


  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.itheima.web.servlet.HelloServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/HelloServlet</url-pattern>
  </servlet-mapping>    

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>
</web-app>

HelloServlet.java

package com.itheima.web.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.itheima.service.AccountService;

public class HelloServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        // 從application作用域(ServletContext)獲得spring容器
        // 方式1: 手動從作用域獲取
        ApplicationContext applicationContext = (ApplicationContext) this.getServletContext()
                .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        // 方式2:通過工具類獲取
        ApplicationContext apppApplicationContext2 = WebApplicationContextUtils
                .getWebApplicationContext(this.getServletContext());

        // 轉賬操作
        AccountService accountService = (AccountService) applicationContext.getBean("accountService");
        accountService.transfer("jack", "rose", 1000);
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

四、SSH 整合

4.1、jar包 整合

  • struts :2.3.15.3
  • spring : 3.2.0
  • hibernate : 3.6.10

4.1.1、struts

  • struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib
  • 模板技術,一般用於頁面的靜態化
    • 1、jsp : *.jsp
    • 2、freemarker :擴展名 :*.ftl
    • 3、velocity :擴展名 :*.vm

4.1.2、spring

  • 基礎 :4 + 1 :beans、core、context、expression + commons-logging (struts中已經導入了)
  • AOP :aop聯盟、spring aop 、aspect規範、spring aspect
  • db :jdbc、tx
  • 測試 :test
  • web開發 :spring web
  • 驅動 :mysql
  • 連接池 :c3p0
  • spring整合hibernate :spring orm

4.1.3、hibernate

  • %h%\hibernate3.jar 核心
  • %h%\lib\required 必須
  • %h%\lib\jpa jpa規範(java persistent api:java持久api) 用於hibernate註解開發(注意:Hibernate3中很少用,Hibernate4中推薦使用註解開發),例如:@Entity、@Id 等等。
  • 整合log4j
    • 導入 log4j…jar (struts中已經導入了)
    • 整合(過渡):slf4j-log4j12-1.7.5.jar
  • 二級緩存
  • 核心:ehcache-1.5.0.jar
  • 依賴:
    • backport-util-concurrent-2.1.jar
    • commons-logging (struts中已經導入了)

4.1.4、整合包

  • spring 整合hibernate :spring orm
  • struts 整合spring :struts2-spring-plugin-2.3.15.3.jar
  • 刪除重複jar包:

4.2、spring整合hibernate:有hibernate.cfg.xml(最熟悉)

4.2.1、創建表

create table t_user(
    id int primary key auto_increment,
    username varchar(50),
    password varchar(32),
    age int 
);

4.2.2、PO 類(javabean)

User.java

package com.itheima.domain;

public class User {
    /*
     * CREATE TABLE t_user(
          id INT PRIMARY KEY AUTO_INCREMENT,
          username VARCHAR(50),
          PASSWORD VARCHAR(32),
          age INT 
        );
     */ 
    private Integer id;
    private String username;
    private String password;
    private Integer age;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

映射文件User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.itheima.domain.User" table="t_user">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="username"></property>
        <property name="password"></property>
        <property name="age"></property>
    </class>
</hibernate-mapping>

4.2.3、dao層

  • spring提供了 HibernateTemplate對象,用於操作PO對象,類似於之前學習Hibernate時的 Session對象。

UserDao.java

package com.itheima.dao;

import com.itheima.domain.User;

public interface UserDao {
    /**
     * 保存
     * 
     * @param user
     */
    public void save(User user);
}

UserDaoImpl.java

package com.itheima.dao.impl;

import org.springframework.orm.hibernate3.HibernateTemplate;

import com.itheima.dao.UserDao;
import com.itheima.domain.User;

public class UserDaoImpl implements UserDao {

    // 需要Spring注入模板
    private HibernateTemplate hibernateTemplate;
    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }

    @Override
    public void save(User user) {
        this.hibernateTemplate.save(user);
    }
}

4.2.4、service層

UserService.java

package com.itheima.service;

import com.itheima.domain.User;

public interface UserService {
    /**
     * 註冊
     * 
     * @param user
     */
    public void register(User user);
}

UserServiceImpl.java

package com.itheima.service.impl;

import com.itheima.dao.UserDao;
import com.itheima.domain.User;
import com.itheima.service.UserService;

public class UserServiceImpl implements UserService {

    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void register(User user) {
        userDao.save(user);
    }
}

4.2.5、Hibernate的配置文件 hibernate.cfg.xml

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>
        <!-- 1、基本四項 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/day35</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>

        <!-- 2、配置方言 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        <!-- 3、sql語句 -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>

        <!-- 4、自動生成表結構(一般沒用,因爲真正的開發中是先建模,然後通過工具自動生成表結構、SQL語句 等) -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- 5、本地線程綁定(用處不大,因爲事務我們交給Spring管理了) -->
        <property name="hibernate.current_session_context_class">thread</property>

        <!-- 6、導入映射文件 -->
        <mapping resource="com/itheima/domain/User.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

4.2.6、Spring的配置文件 applicationContext.xml

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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/tx 
                              http://www.springframework.org/schema/tx/spring-tx.xsd
                              http://www.springframework.org/schema/aop 
                              http://www.springframework.org/schema/aop/spring-aop.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context.xsd">

     <!-- 1、加載hibenrate.cfg.xml,獲得 SessionFactory(小結:jdbc開發需要數據源DataSource,hibernate開發需要SessionFactory)
             * configLocation  確定配置文件位置
     -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
    </bean>

    <!-- 2、先創建模板  
             * 需要Spring注入模板,該底層使用的就是session,而session是由SessionFactory獲得的
    -->
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

    <!-- 3、dao -->
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
        <property name="hibernateTemplate" ref="hibernateTemplate"></property>
    </bean>

    <!-- 4、service -->
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

    <!-- 5、事務管理
         5.1、事務管理器 :HibernateTransactionManager 
    -->
    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" >
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <!-- 5.2、事務詳情 ,給ABC進行具體的事務設置 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="register"/>
        </tx:attributes>
    </tx:advice>
    <!-- 5.3、AOP編程,從 ABCD 業務中 篩選出 ABC -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/>
    </aop:config>   

</beans> 

4.2.7、測試

TestApp.java

package com.itheima;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.itheima.domain.User;
import com.itheima.service.UserService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class TestApp {

    @Autowired
    private UserService userService;

    @Test
    public void demo01(){
        User user = new User();
        user.setUsername("bruce");
        user.setPassword("123456");
        user.setAge(26);

        userService.register(user);
    }
}

4.3、spring整合hibernate:沒有hibernate.cfg.xml【最常用】

  • 刪除hibernate.cfg.xml文件,但需要保存文件內容,將其配置到spring中
  • 修改dao層,繼承HibernateDaoSupport,spring中刪除模板,給dao注入SessionFactory

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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                              http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/tx 
                              http://www.springframework.org/schema/tx/spring-tx.xsd
                              http://www.springframework.org/schema/aop 
                              http://www.springframework.org/schema/aop/spring-aop.xsd
                              http://www.springframework.org/schema/context 
                              http://www.springframework.org/schema/context/spring-context.xsd">
     <!-- 1.1、加載properties文件 -->

     <!-- 1.2、配置數據源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///day35"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

    <!-- 1.3、配置 LocalSessionFactoryBean,獲得SessionFactory 
        * configLocation確定配置文件位置
            <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
        1)dataSource    數據源
        2)hibernateProperties   hibernate其他配置項
        3)導入映射文件
            mappingLocations ,確定映射文件位置,需要加“classpath:”。支持通配符 【常使用】
                <property name="mappingLocations" value="classpath:com/itheima/domain/User.hbm.xml"></property> 或者
                <property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property>
            mappingResources ,加載指定的映射文件,默認從src下開始,不需要加“classpath:” 。不支持通配符*
                <property name="mappingResources" value="com/itheima/domain/User.hbm.xml"></property>
            mappingDirectoryLocations ,加載指定目錄下的,所有配置文件
                <property name="mappingDirectoryLocations" value="classpath:com/itheima/domain/"></property>
            mappingJarLocations ,從jar包中獲得映射文件
    -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</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">thread</prop>
            </props>
        </property>
        <property name="mappingLocations" value="classpath:com/itheima/domain/*.hbm.xml"></property>
    </bean>

    <!-- 3、dao spring中刪除模板,給dao注入SessionFactory-->
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

    <!-- 4、service -->
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>

    <!-- 5 、事務管理
         5.1、 事務管理器 :HibernateTransactionManager 
    -->
    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" >
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <!-- 5.2 、事務詳情 ,給ABC進行具體的事務設置 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="register"/>
        </tx:attributes>
    </tx:advice>
    <!-- 5.3、AOP編程,從 ABCD 業務中 篩選出 ABC -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"/>
    </aop:config>   

</beans> 

UserDaoImpl.java

package com.itheima.dao.impl;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.itheima.dao.UserDao;
import com.itheima.domain.User;

// 底層需要SessionFactory,會自動創建HibernateTemplate模板
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

    @Override
    public void save(User user) {
        this.getHibernateTemplate().save(user);
    }
}

4.4、struts整合spring:由spring去創建action

操作步驟如下:

  1. 編寫action類,並將其配置給spring,由spring去創建action,並使spring注入service
  2. 編寫struts.xml
  3. 編寫表單jsp頁面
  4. web.xml 配置
    1. 確定Spring xml配置文件位置 contextConfigLocation
    2. 配置spring 的監聽器 ContextLoaderListener
    3. 配置struts 的前端控制器 StrutsPrepareAndExecuteFilter

4.4.1、action類

UserAction.java

package com.itheima.web.action;

import com.itheima.domain.User;
import com.itheima.service.UserService;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class UserAction extends ActionSupport implements ModelDriven<User> {

    // 1、封裝數據
    private User user = new User();

    @Override
    public User getModel() {
        return user;
    }

    // 2、service
    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    /**
     * 註冊功能
     * 
     * @return
     */
    public String register() {
        userService.register(user);
        return "success";
    }
}

4.4.2、spring配置

applicationContext.xml

......
    <!-- 6、配置action,並配置多例 -->
    <bean id="userAction" class="com.itheima.web.action.UserAction" scope="prototype">
        <property name="userService" ref="userService"></property>
    </bean>
......

4.4.3、struts配置

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.devMode" value="true" />

    <package name="default" namespace="/" extends="struts-default">
        <!-- 底層自動從spring容器中通過名稱獲得內容, getBean("userAction") -->
        <action name="userAction_*" class="userAction" method="{1}">
            <result name="success">/message.jsp</result>
        </action>
    </package>
</struts>

4.4.4、jsp表單

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}/userAction_register" method="post">
        用戶名:<input type="text" name="username"/><br/>
        密碼:<input type="password" name="password"/><br/>
        年齡:<input type="text" name="age"/><br/>
        <input type="submit" />
    </form> 
</body>
</html>

4.4.5、配置web.xml

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_2_5.xsd"
    id="WebApp_ID" version="2.5">

    <!-- 1、確定Spring xml的位置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <!-- 2、spring 的監聽器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 3、struts 的前端控制器 -->
    <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>
</web-app>

4.5、struts整合spring:由struts去創建action【最常用】

操作步驟如下:

  • 刪除spring的action配置
  • struts的< action class="全限定類名">

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.devMode" value="true" />

    <package name="default" namespace="/" extends="struts-default">
        <!-- 底層自動從spring容器中通過名稱獲得內容, getBean("userAction") -->
        <action name="userAction_*" class="com.itheima.web.action.UserAction" method="{1}">
            <result name="success">/message.jsp</result>
        </action>
    </package>
</struts>
  • 要求:在Action類中,必須提供service名稱與spring配置文件一致。(如果名稱一致,將自動注入)
  • 1、struts 的配置文件
    • default.properties ,常量配置文件
    • struts-default.xml ,默認核心配置文件
    • struts-plugins.xml ,插件配置文件
    • struts.xml ,自定義核心配置文件
      • 常量的使用時,後面配置項,將覆蓋前面的。
  • 2、default.properties ,此配置文件中確定了按照【名稱】自動注入,如下圖所示:
    • 位置:struts2-core-2.3.15.3.jar/org/apache/struts2/default.properties
  • 3、struts-plugins.xml ,struts整合spring

打開struts-plugins.xml文件後,有如下一句代碼: < constant name="struts.objectFactory" value="spring" /> 該配置說明:struts的action將由spring創建 綜上所述:之後的action由spring創建,並按照名稱自動注入

五、練習要求

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章