天天看點

Spring AOP及事務控制小結一、AOP和OOP差別:二、JAVA動态代理基本文法三、Spring簡化面向切面程式設計

Spring AOP以及事務控制總結

  • 一、AOP和OOP差別:
  • 二、JAVA動态代理基本文法
  • 三、Spring簡化面向切面程式設計
    • 1.AOP基本概念
      • 1)專業術語
    • 2.需要導入的包
    • 3.注解配置AOP過程
      • 1)spring中共有五種通知:
      • 2)五種通知的執行順序
      • 3)多切面類下通知的執行順序
      • 4)切面表達式寫法
      • 5)示例
    • 4.AOPxml配置
    • 5.Spring事物控制
      • 1)Jdbc使用示例
      • 2)事物管理器注解配置
        • (1)事物管理器注解配置内容
        • (2)@Transactional屬性
      • 3)事物管理器XML配置

一、AOP和OOP差別:

AOP和OOP定義:

AOP:(Aspect Oriented Programming)面向切面程式設計

OOP:(Object Oriented Programming )面向對象程式設計

面向切面程式設計:基于OOP基礎之上新的程式設計思想,指在程式運作期間,将某段代碼動态的切入到指定方法的指定位置進行運作的程式設計方式

二、JAVA動态代理基本文法

動态代理的本質是實作了與被代理類同一組接口的類,是以要保證代理類和被代理類實作了同一組接口

舉例:

//接口
public interface Calculatorable {
    Integer add(int a,int b);
    Integer sub(int a,int b);
}

//被代理類
public class Calculator implements Calculatorable {
    public Integer add(int a, int b) {
        System.out.println("加法...");
        return a+b;
    }

    public Integer sub(int a, int b) {
        System.out.println("減法...");
        return a-b;
    }
}

//代理類
public class CalculatorProxy {
    public static Calculatorable getProxy(final Calculatorable cal){
        InvocationHandler ih = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object o = null;
                try {
                    System.out.println("方法執行前...");
                   o= method.invoke(cal, args);
                    System.out.println("方法執行後");
                } catch (Exception e) {
                    System.out.println("方法有異常");

                }  finally {
                    System.out.println("finally語句塊...");
                }
                return o;
            }
        };
        return (Calculatorable) Proxy.newProxyInstance(cal.getClass().getClassLoader(), cal.getClass().getInterfaces(), ih);
    }
}
//測試
    @Test
    public void test01(){
        Calculatorable p = CalculatorProxy.getProxy(new Calculator());
        p.add(4, 5);

    }
   
           

列印結果:

方法執行前...
加法...
方法執行後
finally語句塊...
           

動态代理的缺點:代碼量多,且必須要實作一組接口

三、Spring簡化面向切面程式設計

1.AOP基本概念

1)專業術語

連接配接點:每一個方法的每一個位置

橫切關注點:每一個方法的同一位置下的連接配接點

切入點:需要真正執行切入的方法的位置

切入點表達式:通過表達式篩選切入點

通知方法:每一個橫切關注點下的方法

切面類:橫切關注點+通知方法

2.需要導入的包

Spring AOP需要導入的包:

spring核心包:

commons-logging-1.1.3.jar

spring-aop-4.0.0.RELEASE.jar

spring-beans-4.0.0.RELEASE.jar

spring-context-4.0.0.RELEASE.jar

spring-core-4.0.0.RELEASE.jar

spring-expression-4.0.0.RELEASE.jar

spring-aspects-4.0.0.RELEASE.jar:(Spring支援面向切面程式設計的包)

加強版的面向切面程式設計,即不用實作接口也能實作動态代理(使用cglib)

com.springsource.net.sf.cglib-2.2.0.jar

com.springsource.org.aopalliance-1.0.0.jar

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

maven工程的pom配置(加強版):

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
           

3.注解配置AOP過程

spring實作AOP其實就是依靠切面類,實作一個切面類,切面類由通知方法和切面表達式構成

1)spring中共有五種通知:

@Before:前置通知,在方法執行之前執行

@After:後置通知,在方法執行之後執行

@AfterRunning:傳回通知,在方法傳回結果之後執行

@AfterThrowing:異常通知,在方法抛出異常之後執行

@Around:環繞通知,圍繞着方法執行

2)五種通知的執行順序

方法正常運作:

環繞前置–前置通知–目标方法–環繞傳回–環繞後置–後置通知–傳回通知

方法異常運作:

環繞前置–前置通知–目标方法–環繞異常–環繞後置–後置通知–異常通知

執行順序:

try{

環繞前置

[普通前置]

環繞執行:目标方法執行

環繞傳回

}catch(){

環繞出現異常

}finally{

環繞後置

}

[普通後置]

[普通方法傳回/方法異常]

總之,就是環繞通知要優先比其他的通知先執行,而且執行順序是正常的,即傳回通知在後置通知之前執行

3)多切面類下通知的執行順序

與Filter一樣,先進來的切面類後出去,且各個切面類的環繞通知互相獨立運作,互不影響。即隻會優先本類的其他通知,但并不會影響其他切面類的通知。

4)切面表達式寫法

固定格式: execution(通路權限符 傳回值類型 方法全類名(參數表))

通配符:

  • *:
    1)比對一個或者多個字元:execution(public int com.atguigu.impl.MyMath*r.*(int, int)
     	----MyMath*r表示類名為以mymath開頭,以r結尾
     	2)比對任意一個參數:第一個是int類型,第二個參數任意類型;(比對兩個參數)
     		execution(public int com.atguigu.impl.MyMath*.*(int, *))
     	3)隻能比對一層路徑
     	4)通路權限位置不能寫*;權限位置不寫就行就預設表示public
               
  • …:
    1)比對任意多個參數,任意類型參數
     	2)比對任意多層路徑:
     		execution(public int com.atguigu..MyMath*.*(..));
               
  • &&”、“||”、“!

&&:我們要切入的位置滿足這兩個表達式

MyMathCalculator.add(int,double)

execution(public int com.atguigu…MyMath*.(…))&&execution( .(int,int))

||:滿足任意一個表達式即可

execution(public int com.atguigu…MyMath*.(…))&&execution( .(int,int))

!:隻要不是這個位置都切入

!execution(public int com.atguigu…MyMath*.*(…))

注意:不能以…開頭,如果要寫成最模糊比對,則可寫成execution(* *.*(…)):表示任意包下任意類,如果以*開頭叫表示多層任意路徑

5)示例

目标類:

@Service
public class MyCalculator /*implements MyCalculatorable  可寫可不寫 */ {

    public Integer add(int a, int b) {
        System.out.println("------加法運作了------");
        return a+b;
    }
    public Integer sub(int a, int b) {
        System.out.println("------減法運作了------");
        return a-b;
    }
}
           

切面類A:

@Order(1)  //如果有多切面類,可以指定順序,值越小順序越優先
@Aspect  //表明為切面類
@Component
public class AopTestClass {
    //切面表達式可重用:切面表達式同一寫在一個無傳回值的方法上,用@Pointcut标注
    @Pointcut("execution(Integer com.proxytest.MyCalculator.*(..))")
    public void pointCut(){};


    @Before("pointCut()")
    public void logBefore(JoinPoint jp){
        System.out.println(jp.getSignature().getName()+"方法"+"前置通知開始執行");
    }

    @After("pointCut()")
    public void logAfter(JoinPoint jp){
        System.out.println(jp.getSignature().getName()+"方法"+"後置通知開始執行");
    }

    //在通知方法中寫的參數spring要能識别,是以不能寫spring不能識别的參數
    //JoinPoint:封裝了目前目标方法的詳細資訊
    //throwing:告訴Spring哪個參數是用來接收異常
    //returning:告訴Spring這個result用來接收傳回值
    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void logAfterThrowing(JoinPoint jp,Exception ex){
        System.out.println(jp.getSignature().getName()+"方法"+"異常通知開始執行");
    }

    @AfterReturning(value = "pointCut()",returning = "res")
    public void logAfterReturning(JoinPoint jp,Integer res){
        System.out.println(jp.getSignature().getName()+"方法"+"傳回通知開始執行"+"傳回值為"+res);
    }

    //如果在環繞通知中就把異常捕獲而不抛出,外界是感受不到異常的,是以一般需要抛出,同理,如果方法有傳回值,也需要return
    //注意此處的傳回值不能寫在finally語句中,否則外界還是會将感受不到異常
    @Around("pointCut()")
    public Object logAround(ProceedingJoinPoint jp)  {
       Object res = null;
        try {
            System.out.println(jp.getSignature().getName()+"方法"+"環繞通知的前置通知開始執行.....");
            res = (Integer) jp.proceed(jp.getArgs());
            System.out.println(jp.getSignature().getName()+"方法"+"環繞通知的傳回通知開始執行.....");
            return res;
        } catch (Throwable ex) {
            System.out.println(jp.getSignature().getName()+"方法"+"環繞通知的異常通知開始執行.....");
            throw new RuntimeException(ex);

        }finally {
            System.out.println(jp.getSignature().getName()+"方法"+"環繞通知的後置通知開始執行.....");

        }
    }
}
           

切面類B:

@Order(2)
@Aspect
@Component
public class AopTest02Class {
    @Pointcut("execution(Integer com.proxytest.MyCalculator.*(..))")
    public void pointCut() {}
    @Before("pointCut()")
    public void logBefore(JoinPoint jp) {
        System.out.println(jp.getSignature().getName() + "方法" + "AopTest02Class前置通知開始執行");
    }

    @After("pointCut()")
    public void logAfter(JoinPoint jp) {
        System.out.println(jp.getSignature().getName() + "方法" + "AopTest02Class後置通知開始執行");
    }

    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void logAfterThrowing(JoinPoint jp, Exception ex) {
        System.out.println(jp.getSignature().getName() + "方法" + "AopTest02Class異常通知開始執行");
    }

    @AfterReturning(value = "pointCut()", returning = "res")
    public void logAfterReturning(JoinPoint jp, Integer res) {
        System.out.println(jp.getSignature().getName() + "方法" + "AopTest02Class傳回通知開始執行" + "傳回值為" + res);
    }
}
           

方法正常運作控制台列印結果:

add方法環繞通知的前置通知開始執行.....
add方法前置通知開始執行
add方法AopTest02Class前置通知開始執行
------加法運作了------
add方法AopTest02Class後置通知開始執行
add方法AopTest02Class傳回通知開始執行傳回值為11
add方法環繞通知的傳回通知開始執行.....
add方法環繞通知的後置通知開始執行.....
add方法後置通知開始執行
add方法傳回通知開始執行傳回值為11
           

方法異常運作控制台列印結果:

add方法環繞通知的前置通知開始執行.....
add方法前置通知開始執行
add方法AopTest02Class前置通知開始執行
------加法運作了------
add方法AopTest02Class後置通知開始執行
add方法AopTest02Class異常通知開始執行
add方法環繞通知的異常通知開始執行.....
add方法環繞通知的後置通知開始執行.....
add方法後置通知開始執行
add方法異常通知開始執行
           

測試類:

@Test
    public void test02(){
        ApplicationContext ac= new ClassPathXmlApplicationContext("spring.xml");
        MyCalculator bean = (MyCalculator)ac.getBean(MyCalculator.class);
        Integer i = bean.add(5,6);

    }
           

開始AOP注解配置:

<context:component-scan base-package="com.*"/>
<aop:aspectj-autoproxy/>
           

注意:

從ioc容器中拿到目标對象時:

1)如果實作了接口

如果用類類型擷取執行個體,一定用他的接口類型,不要用它的實作類,也可以通過真實對象的id來擷取

MyCalculatorablebean = ioc.getBean(MyCalculatorable.class);
MyCalculatorbean = (MyCalculator) ioc.getBean("myCalculator");
           

2)如果直接寫的目标類,則根據目标類類型和id名稱擷取對象都可

MyCalculatorbean = (MyCalculator) ioc.getBean(MyCalculator.class);
MyCalculatorbean = (MyCalculator) ioc.getBean("myCalculator");
           

4.AOPxml配置

<!--  1.配置切面類和目标類的Bean-->
    <bean id="myCalculator" class="com.proxytest.MyCalculator"></bean>
    <bean id="aopTestClass" class="com.aoptest.AopTestClass"></bean>
    <bean id="aopTest02Class" class="com.aoptest.AopTest02Class"></bean>


    <!-- 2.引入AOP名稱空間,配置切入點表達式 -->
    <aop:config>
        <aop:pointcut expression="execution(Integer com.proxytest.MyCalculator.*(..))" id="globalPoint"/>


        <!-- 普通前置  ===== 目标方法  =====(環繞執行後置/傳回)====s普通後置====普通傳回    -->
        <!-- 指定切面:@Aspect -->
        <aop:aspect ref="aopTestClass" order="1">
            <!-- 配置哪個方法是前置通知;method指定方法名,[email protected]("切入點表達式")-->

            <!-- 如果切入點定義在目前切面類中,就隻能目前切面能用 -->
            <aop:pointcut expression="execution(Integer com.proxytest.MyCalculator.*(..))" id="mypoint"/>
            <aop:around method="logAround" pointcut-ref="mypoint"/>
            <aop:before method="logBefore" pointcut="execution(Integer com.proxytest.MyCalculator.*(..))"/>
            <aop:after-returning method="logAfterReturning" pointcut-ref="mypoint" returning="res"/>
            <aop:after-throwing method="logAfterThrowing" pointcut-ref="mypoint" throwing="ex"/>
            <aop:after method="logAfter" pointcut-ref="mypoint"/>
        </aop:aspect>


        <aop:aspect ref="aopTest02Class" order="3">
            <aop:before method="logBefore" pointcut-ref="globalPoint"/>
            <aop:after-returning method="logAfterReturning" pointcut-ref="globalPoint" returning="res"/>
            <aop:after-throwing method="logAfterThrowing" pointcut-ref="globalPoint" throwing="ex"/>
            <aop:after method="logAfter" pointcut-ref="globalPoint"/>
        </aop:aspect>
        <!-- 在切面類中使用五個通知注解來配置切面中的這些通知方法都何時何地運作 -->
    </aop:config>

 tips:注意,在xml中配置,環繞前置執行的位置不一定會比普通前置要前面,這取決于aop:around和aop:before标簽的位置順序,但是環繞後置一定比普通後置先
           

5.Spring事物控制

1)Jdbc使用示例

Spring提供了JdbcTemplate能快捷的操作資料庫

JdbcTemplate使用步驟:

1)、導包;

spring-jdbc-4.0.0.RELEASE.jar

spring-orm-4.0.0.RELEASE.jar

spring-tx-4.0.0.RELEASE.jar

2)、寫配置

<!--引入外部配置檔案  -->
    <context:property-placeholder location="classpath:dbconfig.properties"/>

    <!-- 實驗1:測試資料源
    ${}取出配置檔案中的值
    #{}Spring的表達式語言
     -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>

    <!-- Spring提供了一個類JdbcTemplate,我們用它操作資料庫;
        導入Spring的資料庫子產品
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
         -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>
           

3)、測試

public class TxTest {

    ApplicationContext ioc = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);
    NamedParameterJdbcTemplate namedJdbcTemplate =ioc.getBean(NamedParameterJdbcTemplate.class);
    @Test
    public void test() throws SQLException {
        DataSource bean = ioc.getBean(DataSource.class);//接口類型擷取執行個體
        Connection connection = bean.getConnection();
        System.out.println(connection);
        connection.close();
    }

    @Test
    public void test01() throws SQLException {
        System.out.println(jdbcTemplate);
    }
    /**
     * 實驗2:将emp_id=5的記錄的salary字段更新為1300.00
     */
    @Test
    public void test02(){
        String sql = "UPDATE employee SET salary=? WHERE emp_id=?";
        int update = jdbcTemplate.update(sql, 1300.00,5);
        System.out.println("更新員工:"+update);
        /**
     * 實驗3:批量插入:batchUpdate
     */
    @Test
    public void test03(){
        String  sql ="INSERT INTO employee(emp_name,salary) VALUES(?,?)";
        //List<Object[]>
        //List的長度就是sql語句要執行的次數
        //Object[]:每次執行要用的參數
        List<Object[]> batchArgs = new ArrayList<Object[]>();
        batchArgs.add(new Object[]{"張三",1998.98});
        batchArgs.add(new Object[]{"李四",2998.98});
        batchArgs.add(new Object[]{"王五",3998.98});
        batchArgs.add(new Object[]{"趙六",4998.98});


        int[] is = jdbcTemplate.batchUpdate(sql, batchArgs);
        for (int i : is) {
            System.out.println(i);
        }
    }
    /**
     * 實驗4:查詢emp_id=5的資料庫記錄,封裝為一個Java對象傳回;
     * javaBean需要和資料庫中字段名一緻,否則無法完成封裝;
     *
     * jdbcTemplate在方法級别進行了區分
     * 查詢集合:jdbcTemplate.query()
     * 查詢單個對象:jdbcTemplate.queryForObject()
     *         如果查詢沒結果就報錯;
     *
     */
    @Test
    public void test04(){
        String sql = "SELECT emp_id empId,emp_name empName,salary FROM employee WHERE emp_id=?";
        //RowMapper:每一行記錄和javaBean的屬性如何映射
        Employee employee = null;
        try {
            employee = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Employee.class), 50);
        } catch (DataAccessException e) {


        }
        System.out.println(employee);
    }
    /**
     * 實驗5:查詢salary>4000的資料庫記錄,封裝為List集合傳回
     */
    @Test
    public void test05(){
        String sql = "SELECT emp_id empId,emp_name empName,salary FROM employee WHERE salary>?";
        //封裝List;集合裡面元素的類型
        List<Employee> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Employee.class), 4000);


        for (Employee employee : list) {
            System.out.println(employee);
        }
    }
    /**
     * 實驗6:查詢最大salary
     */
    @Test
    public void test06(){
        String sql = "select max(salary) from employee";
        //無論是傳回單個資料還是單個對象,都是調用queryForObject
        Double object = jdbcTemplate.queryForObject(sql, Double.class);
        System.out.println(object);
    }
    /**
     * 實驗7:使用帶有具名參數的SQL語句插入一條員工記錄,并以Map形式傳入參數值
     *其實就是sql語句用的是參數,map的鍵就是參數名,值就是參數,是以隻能更改一條資料
     * 具名參數:(具有名字的參數,參數不是占位符了,而是一個變量名)
     *         文法格-----   :參數名
     * Spring有一個支援具名參數功能的JdbcTemplate:NamedParameterJdbcTemplate
     *
     * 占位符參數:?的順序千萬不能亂。傳參的時候一定注意;
     */
    @Test
    public void test07(){
        String sql = "INSERT INTO employee(emp_name,salary) VALUES(:empName,:salary)";


        //Map
        Map<String, Object> paramMap = new HashMap<>();
        //将所有具名參數的值都放在map中;
        paramMap.put("empName", "田七");
        paramMap.put("salary", 9887.98);
        int update = namedJdbcTemplate.update(sql, paramMap);
        System.out.println(update);
    }
    /**
     * 實驗8:重複實驗7,以SqlParameterSource形式傳入參數值
     */
    @Test
    public void test08(){
        String sql = "INSERT INTO employee(emp_name,salary) VALUES(:empName,:salary)";
        Employee employee = new Employee();
        employee.setEmpName("哈哈");
        employee.setSalary(998.98);


        //
        int i = namedJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(employee));
        System.out.println(i);
    }
    /**
     * 實驗9:建立BookDao,自動裝配JdbcTemplate對象,即建立JdbcTemplate屬性,然後@autowire,再在bookdao中寫入操作資料庫方法
     */
    @Test
    public void test09(){
        EmployeeDao bean = ioc.getBean(EmployeeDao.class);
        Employee employee = new Employee();
        employee.setEmpName("哈哈2");
        employee.setSalary(998.98);
        bean.saveEmployee(employee);
    }
}

           

2)事物管理器注解配置

程式設計式事務:

TransactionFilter{
           try{
                //擷取連接配接
                //設定非自動 送出
                chain.doFilter();
                //送出
           }catch(Exception e){
                //復原
           }finllay{
                //關閉連接配接釋放資源
           }     
}
           

聲明式事務:

    事務管理代碼的固定模式作為橫切關注點,可以通過AOP方法子產品化,進而借助Spring AOP架構實作聲明式事務管理,這個切面類Spring已經實作,這個切面類稱為事物管理器

因為事物控制依賴于切面程式設計,是以之前AOP導入的包,事物控制也需要

maven的POM配置:

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>
           

(1)事物管理器注解配置内容

<context:component-scan base-package="com.*"></context:component-scan>
  <!-- 0、引入外部配置檔案 -->
  <context:property-placeholder location="classpath:pro.properties" />
<!--  配置資料源-->
   <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <property name="user" value="${jdbc.user}"></property>
       <property name="password" value="${jdbc.password}"></property>
       <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
       <property name="driverClass" value="${jdbc.driverClass}"></property>
   </bean>

   <!-- 配置JdbcTemplate -->
   <bean class="org.springframework.jdbc.core.JdbcTemplate">
       <property name="dataSource" ref="pooledDataSource"></property>
   </bean>

   <!-- 事務控制 -->
   <bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <!-- 控制住資料源 -->
       <property name="dataSource" ref="pooledDataSource"></property>
   </bean>
   <!--2:開啟基于注解的事務控制模式;依賴tx名稱空間  -->
   <tx:annotation-driven transaction-manager="tm"/>
   <!--3:給事務方法加注解@Transactional,在service類中的某個方法上加入-->  
           

(2)@Transactional屬性

  • isolation:事物的隔離級别

    事物出現的幾個問題:髒讀、幻讀、不可重複讀

    分别對應以下幾個隔離級别:

    Isolation.READ_UNCOMMITTED:讀出髒資料

    Isolation.READ_COMMITTED:解決髒讀

    Isolation.REPEATABLE_READ:解決髒讀、不可重複讀

    Isolation.SERIALIZABLE:都解決,但效率低,不常用

  • 異常復原機制

    運作時異常(非檢查異常):可以不用處理;預設都復原;

    編譯時異常(檢查異常):要麼try-catch,要麼在方法上聲明throws,預設不復原;

    noRollbackFor:哪些異常事務可以不復原,可以讓原來預設復原的異常給他不復原

    noRollbackFor={ArithmeticException.class,NullPointerException.class}

    noRollbackForClassName:String全類名:

    rollbackFor:哪些異常事務需要復原,原本不復原(原本編譯時異常是不復原的)的異常指定讓其復原

    rollbackForClassName:全類名

  • readOnly-boolean:設定事務為隻讀事務:可以進行事務優化;

         readOnly=true:加快查詢速度;不用管事務那一堆操作了

  • timeout-int(秒為機關):逾時:事務超出指定執行時長後自動終止并復原
  • propagation-Propagation:事務的傳播行為如下表,常用為required和required_new,預設事物都是required
    Spring AOP及事務控制小結一、AOP和OOP差別:二、JAVA動态代理基本文法三、Spring簡化面向切面程式設計

    Tips:

         1.如果任何處崩,已經執行的REQUIRES_NEW都會成功;

         2.如果子事物是REQUIRED,事務的屬性都是繼承于大事務的。即在子事物上設定的如逾時時間等無效,隻有在超事物上設定對子事物才有影響;如果子事物是REQUIRES_NEW則可以自定義事物屬性

3)事物管理器XML配置

<context:component-scan base-package="com.*"></context:component-scan>
  <!-- 0、引入外部配置檔案 -->
  <context:property-placeholder location="classpath:pro.properties" />
<!--  配置資料源-->
   <bean id="pooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <property name="user" value="${jdbc.user}"></property>
       <property name="password" value="${jdbc.password}"></property>
       <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
       <property name="driverClass" value="${jdbc.driverClass}"></property>
   </bean>

   <!-- 配置JdbcTemplate -->
   <bean class="org.springframework.jdbc.core.JdbcTemplate">
       <property name="dataSource" ref="pooledDataSource"></property>
   </bean>

   <!-- 事務控制 -->

   <bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <!-- 控制住資料源 -->
       <property name="dataSource" ref="pooledDataSource"></property>
   </bean>
 <!--
基于xml配置的事務;依賴tx名稱空間和aop名稱空間
  1)、Spring中提供事務管理器(事務切面),配置這個事務管理器
  2)、配置出事務方法的屬性;
  3)、告訴Spring哪些方法是事務方法;
     (事務切面按照我們的切入點表達式去切入事務方法)
 -->

<aop:config>
  <aop:pointcut expression="execution(* com.atguigu.ser*.*.*(..))" id="txPoint"/>
  <!-- 事務建議;事務增強     advice-ref:指向事務管理器的配置 -->
  <aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"/>
</aop:config>

<!--  配置事務管理器:事務建議又稱事務增強;配置的是事務屬性;
transaction-manager="transactionManager":指定是配置哪個事務管理器;-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
     <!--事務屬性  -->
     <tx:attributes>
        <!-- 指明哪些方法是事務方法;切入點表達式隻是說,事務管理器要切入這些方法,哪些方法加事務使用tx:method指定的 -->
        <tx:method name="*"/> <!--可以把所有的切入方法都指定為事物-->
        <tx:method name="checkout" propagation="REQUIRED" timeout="-1"/><!--還可以另外配置某些具體的方法-->
        <tx:method name="get*" read-only="true"/>
     </tx:attributes>
</tx:advice>
           

對于重要的事物/通知方法用XML配置,不重要的用注解配置