spring AOP和事务

1.AOP简介

AOP是在不改原有代码的前提下对代码进行增强

1.1 AOP概念

AOP(Aspect Oriented Programming) 面向切面编程,一种编程范式,指导开发者如何组织程

序结构。

1.2 AOP的作用

在不改原有代码的前提下对代码进行增强。

1.3 AOP的实现方式

JDK动态代理(默认):运行时创建接口的代理实例。

CGLib代码生成库动态代理:采用底层的字节码技术,当目标对象不存在接口时,创建子类代理的实例。

1.4 AOP的核心概念

为了能更好的理解 AOP 的相关概念,我们准备了一个环境,整个环境的内容我们暂时可以不用关

注,最主要的类为 : BookDaoImpl

 
@Repository
public class BookDaoImpl implements BookDao {
 
    public void save() {
        //记录程序当前执行执行(开始时间)
        Long startTime = System.currentTimeMillis();
        //业务执行万次
        for (int i = 0;i<10000;i++) {
            System.out.println("book dao save ...");
        }
        //记录程序当前执行时间(结束时间)
        Long endTime = System.currentTimeMillis();
        //计算时间差
        Long totalTime = endTime-startTime;
        //输出信息
        System.out.println("执行万次消耗时间:" + totalTime + "ms");
    }
 
    public void update(){
        System.out.println("book dao update ...");
    }
 
    public void delete(){
        System.out.println("book dao delete ...");
    }
 
    public void select(){
        System.out.println("book dao select ...");
    }
}

相关依赖

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>

Spring 配置类

@Configuration
@ComponentScan("priv.dandelion")
@EnableAspectJAutoProxy
public class SpringConfig {
}

AOP

package priv.dandelion.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void priv.dandelion.dao.BookDao.*te())")
    private void pt() {
    }

    @Around("pt()")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        //记录程序当前执行执行(开始时间)
        Long startTime = System.currentTimeMillis();
        //业务执行万次
        for (int i = 0;i<10000;i++) {
            // 表示对原始操作的调用
            pjp.proceed();
        }
        //记录程序当前执行时间(结束时间)
        Long endTime = System.currentTimeMillis();
        //计算时间差
        Long totalTime = endTime-startTime;
        //输出信息
        System.out.println("执行万次消耗时间:" + totalTime + "ms");

    }
}

测试类

public class Test {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.save();
        // bookDao.update();
        // bookDao.delete();
        // bookDao.select();
    }
}

AOP的相关术语: 

(1)连接点:在 AOP 中,将所以原始方法称为连接点(如右侧的 save,update,delete和select 方法)。
(2)切入点:在上述四个方法中,除 save 方法之外,仅 update 和 delete 方法执行了万次,即追加了功能。这些追加了同一种功能的方法(可以是一个也可以是多个,此处为两个)称为一个切入点,反之,如 save, select 方法就不是切入点。

切点表达式的写法

语法:

execution([修饰符]返回值类型包名.类名.方法名(参数))

例如:

execution(public void com.iflytek.aop.Target.method())
execution(void com.iflytek.aop.Target.* ( ..))
execution(* com.iflytek.aop.*.*( ..))
execution(* com.iflytek.aop..*.* (..))
execution(* *..*.*(..))
  • 访问修饰符可以省略

  • 返回值类型、包名、类名、方法名可以使用星号*代表任意

  • 包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类

  • 参数列表可以使用两个点..表示任意个数,任意类型的参数列表


(3)通知:共性功能,即左边的方法。

通知的类型

通知的配置语法:

<!--配置文件方式-->
<aop:通知类型 method="切面类中方法名" pointcut="切点表达式"></aop:通知类型>
//注解方式
@通知注解("切点表达式")
名称标签注解说明
前置通知<aop:before>@Before用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知<aop:AfterReturning>@AfterReturning用于配置后置通知。指定增强的方法在切入点方法之后执行
环绕通知<aop:Around>@Around用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行
异常抛出通知<aop:AfterThrowing>@AfterThrowing用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知<aop:After>@After用于配置最终通知。无论增强方式执行是否有异常都会执行

(4)通知类:通知是一个方法,必须写在一个类中。
(5)切面:通知与切入点的关系,一个通知对应一个切入点,称为一个切面。

2.AOP入门案例

2.1 需求分析

使用SpringAOP的注解方式完成在方法执行的前打印出当前系统时间。对于SpringAOP的开发有两种方式,XML 和 注解,我们使用注解。

2.2 思路分析

  1. 导入坐标(pom.xml)
  2. 制作连接点(原始操作,Dao接口与实现类)
  3. 制作共性功能(通知类与通知)
  4. 定义切入点
  5. 绑定切入点与通知关系(切面) 

2.3 环境准备

  • 创建一个Maven项目

  • 项目结构如下:

  • pom.xml添加Spring-context依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>
  • 添加BookDao和BookDaoImpl

public interface BookDao {
    public void save();
    public void update();
}
 
@Repository
public class BookDaoImpl implements BookDao {
 
    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save ...");
    }
 
    public void update(){
        System.out.println("book dao update ...");
    }
}
  •  

    创建Spring的配置类

    @Configuration
    @ComponentScan("com.itheima")
    public class SpringConfig {
    }
  • 编写测试类
  • public class App {
        public static void main(String[] args) {
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
            BookDao bookDao = ctx.getBean(BookDao.class);
            bookDao.save();
        }
    }

     2.4AOP实现步骤

步骤 1:pom.xml 添加依赖

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>

 步骤2:定义接口与实现类

public interface BookDao {
    public void save();
    public void update();
}
 
@Repository
public class BookDaoImpl implements BookDao {
 
    public void save() {
        System.out.println(System.currentTimeMillis());
        System.out.println("book dao save ...");
    }
 
    public void update(){
        System.out.println("book dao update ...");
    }

}

步骤3:aop包下定义通知类和通知

通知就是将共性功能抽取出来后形成的方法,共性功能指的就是当前系统时间的打印。(类名和方法名没有要求,可以任意)

public class MyAdvice {
    //通知,共性功能
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

步骤4:定义切入点@Pointcut,说明该方法待增强

BookDaoImpl中有两个方法,分别是save和update,我们要增强的是update方法,该如何定义呢?

通过@Pointcut指定切入点方法格式和位置

public class MyAdvice {
    //注解描述切入点的方法格式和位置,执行到update时候运行通知。切入点是要被增强的方法
    //切入点表达式的public和异常名可以省略,下面已省略
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    //一个私有空壳方法,切入点依托一个不具有实际意义(无参无返空方法)的方法进行。
    private void pt(){}
    //通知,共性功能
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

说明:

  • 切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑。

步骤5:制作切面@Before或其他通知

@Before是前置通知,通知在原始方法前通知。

public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    //制作切面,这里通知和切入点的关系是before,通知在切入点之前运行
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

 绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置

说明:@Before翻译过来是之前,也就是说通知会在切入点方法执行之前执行,除此之外还有其他四种类型,同理。

步骤6:注解指定该类为bean和切面类@Aspect

@Component
@Aspect
public class MyAdvice {
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
 
    @Before("pt()")
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}

步骤7:配置类注解开启AOP功能@EnableAspectJAutoProxy

 enable译为开启,使能够;EnableAspectJAutoProxy译为开启aspectj自动代理。

@Configuration
@ComponentScan("com.itheima")
//注解开启AOP
@EnableAspectJAutoProxy
public class SpringConfig {
}

步骤8:测试类

public class Test {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookDao bookDao = ctx.getBean(BookDao.class);
        bookDao.update();
    }
}

看到在执行update方法之前打印了系统时间戳,说明对原始方法进行了增强,AOP编程成功。

3.AOP知识小结

3.1 AOP的核心概念

概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
作用:在不惊动原始设计的基础上为方法进行功能增强
核心概念如下:

  • Target(目标对象):代理的目标对象

  • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类

  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring,,这些点指的是方法,因为spring只支持方法类型的连接点

  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

  • Advice(通知/增强):所谓通知是指拦截Joinpoint之后所要做的事情就是通知

  • Aspect(切面):是切入点和通知(引介)的结合

  • Weaving (织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

3.2 切入点表达式

语法:

execution([修饰符]返回值类型包名.类名.方法名(参数))

例如:

execution(public void com.iflytek.aop.Target.method())
execution(void com.iflytek.aop.Target.* ( ..))
execution(* com.iflytek.aop.*.*( ..))
execution(* com.iflytek.aop..*.* (..))
execution(* *..*.*(..))
  • 访问修饰符可以省略

  • 返回值类型、包名、类名、方法名可以使用星号*代表任意

  • 包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类

  • 参数列表可以使用两个点..表示任意个数,任意类型的参数列表

3.3 五种通知类型

名称标签注解说明
前置通知<aop:before>@Before用于配置前置通知。指定增强的方法在切入点方法之前执行
后置通知<aop:AfterReturning>@AfterReturning用于配置后置通知。指定增强的方法在切入点方法之后执行
环绕通知<aop:Around>@Around用于配置环绕通知。指定增强的方法在切入点方法之前和之后都执行
异常抛出通知<aop:AfterThrowing>@AfterThrowing用于配置异常抛出通知。指定增强的方法在出现异常时执行
最终通知<aop:After>@After用于配置最终通知。无论增强方式执行是否有异常都会执行


3.4 通知中获取参数

获取切入点方法的参数,所有的通知类型都可以获取参数
JoinPoint:适用于前置、后置、返回后、抛出异常后通知,设置为第一个参数
ProceedingJoinPoint:适用于环绕通知
获取切入点方法返回值,前置和抛出异常后通知是没有返回值,后置通知可有可无,所以不做研究
返回后通知
环绕通知
获取切入点方法运行异常信息,前置和返回后通知是不会有,后置通知可有可无,所以不做研究
抛出异常后通知
环绕通知

4.事务管理

4.1 Spring事务简介

  • 事务作用:数据层保障一系列的数据库操作同成功同失败
  • Spring事务作用:数据层或业务层保障一系列的数据库操作同成功同失败

4.2 事务的ACID原则 

  1. 事务具有4个基本特性:原子性、一致性、隔离性、持久性。也就是我们常说的ACID原则。

    原子性(Atomicity):一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做例如:A和B两个人一共1000元,A给B转账100元,A付款100元,B收款100元, A的付款行为和B的收款行为要么都成功,要么都失败
    • 一致性(Consistency):

      事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。

      例如:A和B两个人一共1000元,无论A,B两人互相转账多少次,A和B两个人总额都应该是1000元
    • 隔离性(Isolation):

      事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。

      例如:万达影院有《叶问4》电影票100张,允许所有人同时去淘票票上购票,当第100张电影票被A,B,C3人同时购买,如果A拿到第100张电影票,但是还在犹豫要不要付钱,则B,C必须等待A的决定,如果A决定付钱,B.C就无法抢到票,如果A超时不付钱,则这第100张电影票回归票池,从新分配。
    • 持久性(Durability):

      一个事务一旦提交,它对数据库中数据的改变会永久存储起来。其他操作不会对它产生影响

      例如:万达影院有《叶问4》电影票100张,100张电影票销售完毕,对于每个购买者来说,他的购买记录已经产生,即使退票,他之前的购买记录也不会消失。

4.3 事务隔离级别

(1)为什么要事务隔离

如果没有定义事务隔离级别:

  • 脏读 :在一个事务中读取到了另外一个事务修改的【未提交的数据】,而导致多次读取同一个数据返回的结果不一致 (必须要解决的)

    例如:
        1.事务1,小明的原工资为1000, 财务人员将小明的工资改为了8000【但未提交事务】
        2.事务2,小明读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!
        3.事务1,财务发现操作有误,回滚了操作,小明的工资又变为了1000
          像这样,小明读取的工资数8000是一个脏数据
  • 不可重复读 : 在一个事务中读取到了另外一个事务修改的【已提交的数据】,而导致多次读取同一个数据返回的结果不一致

    例如:
        1.事务1,小明读取了自己的工资为1000,操作还没有完成 
        2.事务2,这时财务人员修改了小明的工资为2000,并提交了事务.
        3.事务1,小明再次读取自己的工资时,工资变为了2000
  • 幻读(虚读): 一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录

    例如:   
       1.事务1,财务统计所有工资为5000的员工有10人。
       2.事务2,人事向user表插入了一条员工记录,工资也为5000
       3.事务1,财务再次读取所有工资为5000的员工 共读取到了11条记录,明明刚刚是10人啊?产生幻觉了?

(2)Spring事务隔离级别

隔离级别由低到高【读未提交】=>【读已提交】=>【可重复读】=>【序列化操作】

隔离级别说明脏读不可重复读幻读
ISOLATION_DEFAULTspring默认数据库的隔离级别
ISOLATION_READ_UNCOMMITTED读未提交
ISOLATION_READ_COMMITTED读已提交×
ISOLATION_REPEATABLE_READ可重复读××
ISOLATION_SERIALIZABLE序列化操作×××

4.4 事务传播行为 

  • 指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。

传播行为说明
REQUIRED【默认】当前如果有事务,Spring就会使用该事务;否则会开始一个新事务(增、删、改)
SUPPORTS当前如果有事务,Spring就会使用该事务;否则不会开始一个新事务(查询)
MANDATORY当前如果有事务,Spring就会使用该事务;否则会抛出异常
REQUIRES_NEW当前如果有事务,把当前事务挂起,新建事务
NOT_SUPPORTED当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则该事务挂起
NEVER当前有事务,Spring也会在非事务环境下执行。如果当前有事务,则抛出异常
NESTED当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与REQUIRED一样

4.5 事务的管理方式

  • 编程式事务管理 :编程式事务管理是通过编写代码实现的事务管理。可以根据需求规定事务从哪里开始,到哪里结束,拥有很高的灵活性。但是这种方式,会使业务代码与事务规则高度耦合,难以维护,因此我们很少使用这种方式对事务进行管理。

  • 声明式事务管理: 采用声明的方式来处理事务,可以通过 2 种方式实现,分别是 XML和注解方式。Spring 在进行声明式事务管理时,底层使用了 AOP 。事务管理不侵入开发的组件。

5. 入门案例----转账

5.1 需求分析

A账户减钱,B账户加钱

步骤: 

①:数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)

②:业务层提供转账操作(transfer),调用减钱与加钱的操作

③:提供2个账号和操作金额执行转账操作

④:基于Spring整合MyBatis环境搭建上述操作

5.2 环境搭建

步骤1:准备数据库表

创建数据库gcxy_teach,并按以下sql创建表格

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `account`
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(11) COLLATE utf8mb4_general_ci NOT NULL,
  `money` decimal(10,2) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES ('1', 'messi', '1000.00');
INSERT INTO `account` VALUES ('2', 'pep', '1000.00');

步骤2:创建项目导入jar包

项目的pom.xml添加相关依赖spring相关spring-context,spring-jdbc,spring-test,测试junit,数据库相关mysql-connector-java,druid,mybatis,mybatis-spring
 

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.6</version>
        </dependency>
    </dependencies>

步骤3:根据表创建模型类(实体类)

package com.cqgcxy.entity;

import java.io.Serializable;

public class Account implements Serializable {
    private Integer id;
    private String name;
    private Double money;
 
    public Integer getId() {
        return id;
    }
 
    public void setId(Integer id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public Double getMoney() {
        return money;
    }
 
    public void setMoney(Double money) {
        this.money = money;
    }
 
    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

步骤4:创建Dao接口和实现类

package com.cqgcxy.dao;

public interface AccountDao {
    void inMoney(String name,Double money);
    void outMoney(String name,Double money);
}
package com.cqgcxy.dao.impl;

import com.cqgcxy.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jt;

    public void inMoney(String name, Double money) {
        jt.update("update account set money = money + ? where name = ?",money,name);
    }

    public void outMoney(String name, Double money) {
        jt.update("update account set money = money - ? where name = ?",money,name);
    }
}

步骤5:创建Service接口和实现类

package com.cqgcxy.service;

public interface AccountService {
    /**
     * 转账
     * @param out 转出
     * @param in 转进
     * @param money 金额
     */
    public void transfer(String out,String in,Double money);
}
package com.cqgcxy.service.impl;

import com.cqgcxy.dao.AccountDao;
import com.cqgcxy.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(String out, String in, Double money) {
        accountDao.outMoney(out, money);
        int i=3/0;
        accountDao.inMoney(in, money);
    }
}

步骤6:添加jdbc.properties文件

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///gcxy_teach?serverTimezone=Asia/Shanghai&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456

步骤7:创建JdbcConfig配置类

package com.cqgcxy.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
 
    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
 
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate=new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //配置事务管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

步骤8:创建Spring配置类SpringConfiguration(注解方式)

package com.cqgcxy.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan("com.cqgcxy")
@Import({JdbcConfig.class})
//启动平台事务管理
@EnableTransactionManagement
public class SpringConfiguration {
    
}

步骤9:编写Spring配置文件(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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.cqgcxy"/>

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

    <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">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置通知 事务增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!--配置通知的AOP织入-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.cqgcxy.service.impl.*.*(..))"></aop:advisor>
    </aop:config>
</beans>

步骤10:测试代码

package com.cqgcxy.service.impl;

import com.cqgcxy.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.SpringJUnit4ClassRunner;

import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
//1.xml配置文件方式
@ContextConfiguration("classpath:applicationContext.xml")
//2.注解配置类方式
//@ContextConfiguration(classes = {SpringConfiguration.class})
public class AccountServiceImplTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void transfer() {
        accountService.transfer("messi","pep",200.0);
    }
}

运行结果如图:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值