Spring = Spring詳解Aop使用

一.基於xml的AOP開發:

(1)xml配置詳解:

切點表達式:

表達式語法:

execution([修飾符] 返回值類型 包名.類名.方法名(參數列表))

  • 訪問修飾符可以省略

  • 返回值類型、包名、類名、方法名可以使用星號 * 代替,代表任意

  • 包名與類名之間一個點 . 代表當前包下的類,兩個點 .. 表示當前包及其子包下的類

  • 參數列表可以使用兩個點 .. 表示任意個數,任意類型的參數列表

* 版本一:控制目標對象中,返回值類型void且public修飾的所有方法
	execution(public void cn.itcast.service.impl.AccountServiceImpl.*(..))
	
* 版本二:控制目標對象中,任意修飾符任意返回值的所有方法
	execution(* cn.itcast.service.impl.AccountServiceImpl.*(..))
	
* 版本三:控制service層所有對象的方法	
	execution(* cn.itcast.service..*.*(..))

切點表達式抽取:

(2)通知類型:

通知的配置語法:

<aop:通知類型 method="通知類中方法名" pointcut="切點表達式"></aop:通知類型>

四大通知:

名稱 標籤 說明
前置通知 aop:before 在切入點方法之前執行
後置通知 aop:afterReturning 在切入點方法正常運行之後執行
異常通知 aop:afterThrowing 在切點方法發生異常的時候執行
最終通知 aop:after 無論切入點方法執行時是否有異常,都會執行
@Component
public class MyAdvice {

    // 前置增強
    public void before() {
        System.out.println("前置通知...");
    }

    // 後置增強
    public void afterReturning(){
        System.out.println("後置通知...");
    }

    // 異常增強
    public void afterThrowing(){
        System.out.println("異常通知...");
    }

    // 最終增強
    public void after(){
        System.out.println("最終通知...");
    }
}

注意: 一般情況下,我們不會同時使用四大通知, 因爲xml配置順序可能會打亂我們的執行計劃。

重要: 四大通知一般單獨使用:

環繞通知:

名稱 標籤 說明
環繞通知 aop:around 可以靈活實現四大通知的所有效果

環繞通知的代碼編寫,更貼近於動態代理的底層代碼

注意:測試環繞通知,需要註釋掉四大通知:

    // 環繞通知
    // Proceeding(運行)JoinPoint(連接點)  = 切點
    public void around(ProceedingJoinPoint pjp){

        try {
            System.out.println("前置通知...");

            // 執行切點(調用目標對象原有的方法...)
            pjp.proceed();

            System.out.println("後置通知...");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("異常通知...");
        } finally {
            System.out.println("最終通知...");
        }
    }
<aop:around method="around" pointcut-ref="myPointcut"></aop:around>

總結:

* aop織入的配置
	<aop:config>
        <aop:aspect ref=“通知類”>
            <aop:before method=“通知方法名稱” pointcut=“切點表達式"></aop:before>
        </aop:aspect>
    </aop:config>
                                                        
* 通知的類型
	前置通知、後置通知、異常通知、最終通知
	環繞通知
	
* 切點表達式
	execution([修飾符] 返回值類型 包名.類名.方法名(參數列表))

(2)基於xml的AOP開發

MyAdvice:

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAdvice {

    public void before(){
        System.out.println("前置通知before");
    }


   public void  afterRutruning(){
       System.out.println("後置通知afterRutruning");
   }

    public void  afterThrowing(){
        System.out.println("異常通知afterThrowing");
    }

    public void  after(){
        System.out.println("最後通知after");
    }


    public void around(ProceedingJoinPoint proceedingJoinPoint){

        try {
            System.out.println("前置通知before");

            proceedingJoinPoint.proceed();

            System.out.println("後置通知afterRutruning");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("異常通知afterThrowing");
        }finally {
            System.out.println("最後通知after");
        }
    }
}
@Service
public class AccountServiceImpl implements AccountService {
    @Override
    public void transfer() {
        System.out.println("轉賬了");
    }
}

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

    <!--目標對象,交給ioc-->
    <bean id="accountService" class="com.wsl.sercice.impl.AccountServiceImpl"></bean>

    <!--通知對象,交給ioc-->
    <bean id="myAdvice" class="com.wsl.advice.MyAdvice"></bean>
    <!--aop配置-->
    <aop:config>
        <aop:pointcut id="mypoint" expression="execution(* com.wsl.sercice..*.*(..))"></aop:pointcut>

        <aop:aspect ref="myAdvice">
            <!--<aop:before method="before" pointcut-ref="mypoint"></aop:before>-->
            <!--<aop:after-returning method="afterRutruning" pointcut-ref="mypoint"></aop:after-returning>-->
            <!--<aop:after-throwing method="afterThrowing" pointcut-ref="mypoint"></aop:after-throwing>-->
            <!--<aop:after method="after" pointcut-ref="mypoint"></aop:after>-->

            <aop:around method="around" pointcut-ref="mypoint"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

測試:


@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {

    @Autowired
    AccountService accountService;

    @Test
    public void  test001(){
        accountService.transfer();
    }

}

 

二.基於註解的AOP的開發:

(1)項目結構:

開啓spring的aop註解支持

MyAdvice將通知對象升級爲切面

通知+切點=切面

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAdvice {

//    @Before("execution(* com.wsl.service..*.*(..))")
//    public void before(){
//        System.out.println("前置通知before");
//    }
//
//    @AfterReturning("execution(* com.wsl.service..*.*(..))")
//    public void  afterRutruning(){
//        System.out.println("後置通知afterRutruning");
//    }
//
//    @AfterThrowing("execution(* com.wsl.service..*.*(..))")
//    public void  afterThrowing(){
//        System.out.println("異常通知afterThrowing");
//    }
//
//    @After("execution(* com.wsl.service..*.*(..))")
//    public void  after(){
//        System.out.println("最後通知after");
//    }
@Pointcut("execution(* com.wsl.service..*.*(..))")
public  void myPontCut(){}

    @Around("MyAdvice.myPontCut()")
    public void around(ProceedingJoinPoint proceedingJoinPoint){

        try {
            System.out.println("前置通知before");
            proceedingJoinPoint.proceed();
            System.out.println("後置通知afterRutruning");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("異常通知afterThrowing");
        }finally {
            System.out.println("最後通知after");
        }
    }
}
@Service
public class AccountServiceImpl implements AccountService {
    @Override
    public void transfer() {
//        int i = 9/0;
        System.out.println("anno 轉賬了");
    }
}
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
       	http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
       	http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
	    http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--全註解掃描-->
   <context:component-scan base-package="com.wsl"></context:component-scan>
    <!--aop自動註解配置-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
import com.wsl.service.AccountService;
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.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {


    @Autowired
    AccountService accountService;

    @Test
    public void Test001(){
        accountService.transfer();
    }

}

(2)註解配置詳解:

切點表達式: 語法與xml一致

如何切面類中,抽取切點表達式:

通知類型:

四大通知:

名稱 標籤 說明
前置通知 @Before 在切入點方法之前執行
後置通知 @AfterReturning 在切入點方法正常運行之後執行
異常通知 @AfterThrowing 在切點方法發生異常的時候執行
最終通知 @After 無論切入點方法執行時是否有異常,都會執行

 

注意:使用註解時,四大通知同時開啓的順序:

@Before -- > @After --> @AfterReturning(@AfterThrowing)

註解版本的四大通知,單獨使用...

 

環繞通知:

名稱 標籤 說明
環繞通知 @Around 可以靈活實現四大通知的所有效果

 

(3)純註解配置:

不適用配置文件:

@Component
@Aspect
public class MyAdvice {

//    @Pointcut("execution(* com.wsl.service..*.*(..))")
    @Pointcut("execution(* com.wsl.service..*.*(..))")
    public  void myPontCut(){}

//    @Before("execution(* com.wsl.service..*.*(..))")
//    public void before(){
//        System.out.println("前置通知before");
//    }
//
//    @AfterReturning("execution(* com.wsl.service..*.*(..))")
//    public void  afterRutruning(){
//        System.out.println("後置通知afterRutruning");
//    }
//
//    @AfterThrowing("execution(* com.wsl.service..*.*(..))")
//    public void  afterThrowing(){
//        System.out.println("異常通知afterThrowing");
//    }
//
//    @After("execution(* com.wsl.service..*.*(..))")
//    public void  after(){
//        System.out.println("最後通知after");
//    }


    @Around("MyAdvice.myPontCut()")
    public void around(ProceedingJoinPoint proceedingJoinPoint){

        try {
            System.out.println("前置通知before");
            proceedingJoinPoint.proceed();
            System.out.println("後置通知afterRutruning");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("異常通知afterThrowing");
        }finally {
            System.out.println("最後通知after");
        }
    }
}

配置文件

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.wsl")
@EnableAspectJAutoProxy
public class SpringConfig {
}
@Service
public class AccountServiceImpl implements AccountService {
    @Override
    public void transfer() {
//        int i = 9/0;
        System.out.println("anno 轉賬了");
    }
}
import com.wsl.config.SpringConfig;
import com.wsl.service.AccountService;
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.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountTest {

    @Autowired
    AccountService accountService;

    @Test
    public void test001(){
        accountService.transfer();
    }

}

總結:

* 使用@Aspect註解,標註切面類

* 使用@Before等註解,標註通知方法

* 使用@Pointcut註解,抽取切點表達式

* 配置aop自動代理 <aop:aspectj-autoproxy/> 或 @EnableAspectJAutoProxy

三.AOP優化轉賬案例:

(1)xml實現

在原來項目基礎上,添加這個依賴:

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

相關類:

@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner queryRunner;

    @Autowired
    private ConnectionUtils connectionUtils;

    @Override
    public void outUser( String outUser, Double money) {

        String sql = "update account set money= money-? where name=?";
        try {
            Connection threadConnection = connectionUtils.getThreadConnection();
            queryRunner.update(threadConnection,sql,money,outUser);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void inUser(String inUser, Double money) {
        String sql = "update account set money= money+? where name=?";
        try {
            Connection threadConnection = connectionUtils.getThreadConnection();
            queryRunner.update(threadConnection,sql,money,inUser);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
@Service
public class AccountServiceImpl implements AccountService {


    @Autowired
    private AccountDao accountDao;
    @Override
    public void transfer(String outUser, String inUser, Double money) {

        accountDao.outUser(outUser,money);
        accountDao.inUser(inUser,money);

    }
}
@Component
public class ConnectionUtils {

    @Autowired
    private DataSource dataSource;


    private  static  final ThreadLocal<Connection> t1= new ThreadLocal<>();

    public Connection getThreadConnection(){
        Connection connection = t1.get();
        if (connection==null){
            try {
                connection = dataSource.getConnection();
                t1.set(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }


    public  void removeThreadConnection(){
        t1.remove();
    }
}

通知對象:環繞通知:

@Component
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    public  void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public  void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void release(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(true);
            connectionUtils.getThreadConnection().close();
            connectionUtils.removeThreadConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 環繞通知
    public void aroundTx(ProceedingJoinPoint pjp){
        try {
            beginTransaction();
            pjp.proceed();
            commit();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            rollback();
        } finally {
            release();
        }
    }
}
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
       	http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
       	http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
	    http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--開啓註解組件掃描-->
    <context:component-scan base-package="com.wsl"/>

    <!--加載第三方配置-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--druid連接交給ioc容器-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!--queryRunner交給ioc容器-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>


    <!--aop配置-->
    <aop:config>
        <!--切面-->
        <aop:aspect ref="transactionManager">
            <!--織入 環繞通知-->
            <aop:around method="aroundTx" pointcut="execution(* com.wsl.service..*.*(..))"></aop:around>
        </aop:aspect>
    </aop:config>

</beans>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db
jdbc.username=root
jdbc.password=root

測試

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {

    @Autowired
    private AccountService accountService;


    @Test
    public void test001(){
        accountService.transfer("tom","jerry",100d);
    }
}

(2)常用註解實現版本:

@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner queryRunner;

    @Autowired
    private ConnectionUtils connectionUtils;

    @Override
    public void outUser( String outUser, Double money) {

        String sql = "update account set money= money-? where name=?";
        try {
            Connection threadConnection = connectionUtils.getThreadConnection();
            queryRunner.update(threadConnection,sql,money,outUser);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void inUser(String inUser, Double money) {
        String sql = "update account set money= money+? where name=?";
        try {
            Connection threadConnection = connectionUtils.getThreadConnection();
            queryRunner.update(threadConnection,sql,money,inUser);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
@Service
public class AccountServiceImpl implements AccountService {


    @Autowired
    private AccountDao accountDao;
    @Override
    public void transfer(String outUser, String inUser, Double money) {

        accountDao.outUser(outUser,money);
        accountDao.inUser(inUser,money);

    }
}

環繞通知:

package com.wsl.tx;


import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;

@Component
@Aspect
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    public  void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void commit(){
        try {
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public  void rollback(){
        try {
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void release(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(true);
            connectionUtils.getThreadConnection().close();
            connectionUtils.removeThreadConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    // 環繞通知
    @Around("execution(* com.wsl.service..*.*(..))")
    public void aroundTx(ProceedingJoinPoint pjp){
        try {
            beginTransaction();
            pjp.proceed();
            commit();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            rollback();
        } finally {
            release();
        }
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

    <context:component-scan base-package="com.wsl"></context:component-scan>

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

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>

    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>



    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {


    @Autowired
    AccountService accountService;


    @Test
    public void test001(){
       accountService.transfer("jerry","tom",100d);
    }
}

 

四。Spring的JdbcTemplate

(1)JdbcTemplate是什麼?

JdbcTemplate是Spring的一款用於簡化Dao代碼的工具包,它底層封裝了JDBC技術。

**核心對象**

JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);

核心方法

int update(); 執行增、刪、改語句

List<T> query(); 查詢多個
T queryForObject(); 查詢一個
     RowMapper<>(); ORM映射接口
	 new BeanPropertyRowMapper<>(); 實現ORM映射封裝子類

例如:

查詢數據庫所有賬戶信息到Account實體中

public class JdbcTemplateTest {

    @Test
    public void testFindAll() throws Exception {
        // 創建核心對象
        JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
        // 編寫sql
        String sql = "select * from account";
        // 執行sql
        List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Account.class));
    }
}

(2)入門案例:

依賴座標:mysql驅動、druid連接池、spring-jdbc、spring-context、junit、spring-junit

    <!--依賴管理-->
    <dependencies>
        <!--mysql驅動-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--druid連接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <!--spring-jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <!--spring核心-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--spring整合junit-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>

工具類連接池:

public class JdbcUtils {

    private static DruidDataSource dc =  new DruidDataSource();

    static {
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
        String driverClass = bundle.getString("jdbc.driver");
        String jdbcUrl = bundle.getString("jdbc.url");
        String username = bundle.getString("jdbc.username");
        String password = bundle.getString("jdbc.password");

        dc.setDriverClassName(driverClass);
        dc.setUrl(jdbcUrl);
        dc.setUsername(username);
        dc.setPassword(password);
    }

    public static Connection getConnection() throws SQLException {
        return dc.getConnection();
    }

    public static DataSource getDataSource(){
        return dc;
    }
}

新測試代碼:

public class JdbcTemplateTest {

    // 新增
    @Test
    public void test01() throws Exception {
        // 1.創建JdbcTemplate核心對象
        JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
        // 2.編寫sql
        String sql = "insert into account(name,money) values(?,?)";
        // 3.執行sql
        int i = jdbcTemplate.update(sql, "哈哈頂", 1000d);
    }
}

查詢:

測試案例:

    // id查詢
    @Test
    public void test02() throws Exception {
        // 1.創建JdbcTemplate核心對象
        JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());
        // 2.編寫sql
        String sql = "select * from account where id = ?";
        // 3.執行sql
        Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), 4);

        System.out.println(account);
    }

 

(3)Spring整合JdbcTemplate 以及轉賬案例

編寫AccountDao

import com.wsl.dao.AccountDao;
import com.wsl.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class AccountDaoImpl implements AccountDao {


    @Autowired
    private   JdbcTemplate jdbcTemplate;

    @Override
    public List<Account> findAll() {
        String sql = "select * from account";
        List<Account> accountList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Account.class));
        return accountList;
    }

    @Override
    public int add(Account account) {
        String sql ="insert into account(name,money) values(?,?)";
        int update = jdbcTemplate.update(sql, account.getName(), account.getMoney());
        return update;
    }

    @Override
    public int delete(Integer aid) {
        String sql ="delete from account where id = ? ";
        int update = jdbcTemplate.update(sql, aid);
        return update;
    }

    @Override //update score set chinese=60 where id=?
    public int update(Account account) {
        String sql = "update account set name= ? , money = ? where id= ? ";
        int update = jdbcTemplate.update(sql, account.getName(), account.getMoney(), account.getId());
        return update;
    }

    @Override
    public Account findById(Integer aid) {
        String sql = "select * from account where id = ?";
        Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Account.class), aid);
        return account;
    }

    @Override
    public void outUser(String outUser, Double money) {
        String sql ="update account set money = money - ? where name = ?";
        jdbcTemplate.update(sql,money,outUser);
    }

    @Override
    public void inUser(String inUser, Double money) {
        String sql ="update account set money = money + ? where name = ?";
        jdbcTemplate.update(sql,money,inUser);
    }
}

 

編寫AccountService

import com.wsl.dao.AccountDao;
import com.wsl.domain.Account;
import com.wsl.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.List;

@Service
public class AccountServiceImpl implements AccountService {


    @Autowired
    AccountDao accountDao;


    @Autowired
    private PlatformTransactionManager transactionManager;

    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }

    @Override
    public int add(Account account) {
        return accountDao.add(account);
    }

    @Override
    public int delete(Integer aid) {
        return accountDao.delete(aid);
    }

    @Override
    public int update(Account account) {
        return accountDao.update(account);
    }

    @Override
    public Account findById(Integer aid) {
        return accountDao.findById(aid);
    }

    @Override
    public void transfer(String outUser, String inUser, Double money) {
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setReadOnly(false);
        defaultTransactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);
        try{

            accountDao.outUser(outUser,money);
            accountDao.inUser(inUser,money);

            transactionManager.commit(status);
        }catch (Exception e){
            e.printStackTrace();
            transactionManager.rollback(status);
        }
    }
}

 

編寫Spring配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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">

    <context:component-scan base-package="com.wsl"></context:component-scan>

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

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
</bean>


<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
    <!--<aop:aspectj-autoproxy></aop:aspectj-autoproxy>-->
</beans>

 

測試:

@RunWith(SpringRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class AccountTest {

    @Autowired
    AccountService accountService;

    @Test
    public void test001(){
    //查詢所有
//        List<Account> accountList = accountService.findAll();
//        System.out.println(accountList);

        //查詢id=1的用戶
//        Account account = accountService.findById(1);
//        System.out.println(account);

        //新增用戶
//        Account account1 = new Account();
//        account1.setName("beijing");
//        account1.setMoney(1200.0);
//        accountService.add(account1);

        //修改信息
//        Account account2 = new Account();
//        account2.setName("beijing");
//        account2.setMoney(9100.0);
//        account2.setId(8);
//        accountService.update(account2);

//      accountService.delete(10);


        accountService.transfer("tom","jerry",200d);
    }
}

使用JdbcTemplate,無法操作自定義的事務管理器....

因爲spring當時在設計這套工具包的時候,就要求事務交給spring控制

 

五.Spring的事務:

Spring的事務控制可以分爲編程式事務控制和聲明式事務控制。

編程式事務

  • 就是將業務代碼和事務代碼放在一起書寫,它的耦合性太高,開發中不使用

聲明式事務

  • 其實就是將事務代碼(spring內置)和業務代碼隔離開發, 然後通過一段配置讓他們組裝運行, 最後達到事務控制的目的.

    聲明式事務就是通過AOP原理實現的.

(1)編程式事務:

PlatformTransactionManager

spring事務管理器的頂級接口,裏面提供了我們常用的操作事務的方法

- TransactionStatus getTransaction(TransactionDefinition definition);
    功能:獲取事務的狀態信息

- void commit(TransactionStatus status);
    功能:提交事務

- void rollback(TransactionStatus status);
    功能:回滾事務

 

spring事務管理器先定義好規範,真正的執行者需要實現類完成,導入一個座標 spring-orm

- JpaTransactionManager
	會使用sun公司提供的jpa規範(SpringData-jpa)框架事務管理器
	
- DataSourceTransactionManager
	使用 mybatis、jdbc原生、DbUtils、JdbcTemplate框架事務管理
	
- HibernateTransactionManager
	使用hibernate框架事務管理器

 

TransactionDefinition

spring事務定義參數的接口,比如定義:事務隔離級別、事務傳播行爲等等

① 事務隔離級別

isolation:事務的隔離級別

 * ISOLATION_DEFAULT			使用數據庫默認級別
 	MySQL:ISOLATION_REPEATABLE_READ 可重複讀
 	Oracle:ISOLATION_READ_COMMITTED 讀已提交
 
 * ISOLATION_READ_UNCOMMITTED	讀未提交
 
 * ISOLATION_READ_COMMITTED		讀已提交
 
 * ISOLATION_REPEATABLE_READ	可重複讀
 
 * ISOLATION_SERIALIZABLE		串行化

② 事務傳播行爲

事務傳播行爲指的就是當一個業務方法【被】另一個業務方法調用時,應該如何進行事務控制。

比如:

方法A、方法B【主語】

方法A去調用方法B

** REQUIRED(默認傳播行爲)
	如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。
	如果單獨調用方法B時,沒有事務,spring就給當前方法創建一個新事物
	如果方法A中已經存在了事務,調用方法B時,方法B加方法A的事務中....

** SUPPORTS
	支持當前事務,如果當前沒有事務,就以非事務方式執行
	如果單獨調用方法B時沒有事務,咱們就以非事務方法運行
	如果方法A中已經存在了事務,調用方法B時,方法B加方法A的事務中....
--------------------------------------------------------------------------------------
* MANDATORY
	使用當前的事務,如果當前沒有事務,就拋出異常
	
* REQUERS_NEW
	新建事務,如果當前在事務中,把當前事務掛起

* NOT_SUPPORTED
	以非事務方式執行操作,如果當前存在事務,就把當前事務掛起
	
* NEVER
	以非事務方式運行,如果當前存在事務,拋出異常
	
* NESTED
	如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行 REQUIRED 類似的操作

③ 是否只讀

* read-only       只讀事務(增 刪  改不能使用,只能查詢使用)

④ 超時時間

* timeout         默認值是-1,沒有超時限制。如果有,以秒爲單位進行設置

 

TransactionStatus

獲取spring當前事務運行的狀態。

 

總結:

Spring中的事務控制主要就是通過這三個API實現的

* PlatformTransactionManager 負責事務的管理,它是個接口,其子類負責具體工作

* TransactionDefinition 定義了事務的一些相關參數

* TransactionStatus 代表事務運行的一個實時狀態

可以簡單的理解三者的關係:事務管理器通過讀取事務定義參數進行事務管理,然後會產生一系列的事務狀態

(2)使用編程式事務

配置事務管理器:

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

修改service層代碼:

@Service
public class xxxServiceImpl implements xxxService{


    @Autowired
    private PlatformTransactionManager transactionManager;

    public void method() {
        
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 設置是否只讀,爲false才支持事務
        def.setReadOnly(false);
        // 設置隔離級別
        def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        // 設置事務的傳播行爲
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 對事務管理器進行配置
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            // 業務操作
          
            

            // 提交事務
            transactionManager.commit(status);
        } catch (Exception e) {
            e.printStackTrace();
            // 回滾事務
            transactionManager.rollback(status);
        }
    }
}

 

 

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