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);
        }
    }
}

 

 

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