Spring——面向切面编程(AOP)

1 AOP概述

  AOP 并不是 Spring 框架的专属名称,它的全称是 Aspect Oriented Programming ,意为:面向切面编程。
  在程序运行某个方法的时候,不修改原始执行代码逻辑,由程序动态地执行某些额外的功能,对原有的方法做增强,这就叫做面向切面编程。
  在学习AOP之前,建议先学习一下设计模式中的代理模式。

2 术语解释

Join point(连接点)
  连接点是指那些被拦截到的点。在 Spring 中这些点指的是方法,可以看作正在访问的,或者等待访问的那些需要被增强功能的方法。Spring 只支持方法类型的连接点。

Pointcut(切入点)
  切入点是一个规则,定义了我们要对哪些 Joinpoint 进行拦截。因为在一个程序中会存在很多的类,每个类又存在很多的方法,而哪些方法会应用 AOP 对该方法做功能增强呢?这就需要依据我们配置的切入点规则。

Advice(通知)
  拦截到 Joinpoint 之后所要做的事情就是通知。 也就是对方法做的增强功能。

通知分类
  前置通知:在连接点之前运行的通知类型,它不会阻止流程进行到连接点,只是在到达连接点之前运行该通知内的行为,当然 —— 除非它引发异常;
  后置通知:在连接点正常完成后要运行的通知,正常的连接点逻辑执行完,会运行该通知,当然 —— 方法正常返回而没有引发异常;
  最终通知:无论连接点执行后的结果如何,正常还是异常,都会执行的通知;
  异常通知:如果连接点执行因抛出异常而退出,则执行此通知;
  环绕通知:环绕通知可以在方法调用之前和之后执行自定义行为。

Target (目标)
  Target 指的是代理的目标对象,也就是连接点所在的类。

Aspect(切面)
  切面本质是一个类,只不过是个功能类,作为整合 AOP 的切入点和通知。一般来讲,需要在 Spring 的配置文件中配置,或者通过注解来配置。

Weaving(织入)
  织入是一种动作的描述,在程序运行时将增强的功能代码也就是通知,根据通知的类型(前缀后缀等…)放到对应的位置,生成代理对象。

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

3 Spring AOP 的代理模式

  代理名词解释为:以被代理人名义,在授权范围内与第三方实施行为。而在软件行业中代理模式是一种非常常用的设计模式,跟现实生活中的逻辑一致。
  在开发中代理模式的表现为:创建带有现有对象的代理对象以便向外界提供功能接口。代理对象可以为委托对象执行一些附带的,增加的额外功能。

3.1 代理模式分类

  在开发中,实现代理模式可以分为两种。
  静态代理:若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理 ,这种情况下的代理类通常都是我们在 Java 代码中定义的, 静态代理中的代理类和委托类会实现同一接口;

  动态代理:代理类在程序运行时创建的代理方式被称为动态代理。 也就是说,这种情况下,代理类并不是在 Java 代码中定义的,而是在运行时根据我们在 Java 代码中的 “指示” 动态生成的。

3.2 静态代理示例

  userService 接口代码

public interface UserService {
    public void saveUser();
}

  userServiceImpl 实现类代码

@Service
public class UserServiceImpl implements  UserService {

    public void saveUser() {

        System.out.println("执行service中的保存逻辑");
    }
}

  userServiceProxy 代理类代码

public class UserServiceProxy implements UserService {
    private UserService userService;

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public void saveUser() {
        System.out.println("委托类执行saveUser方法之前的逻辑代码");
        userService.saveUser();
        System.out.println("委托类执行saveUser方法之后的逻辑代码");
    }
}

代码解析:
  userServiceProxy 代理类中的属性为委托类的接口对象,目的是在构造方法中接收委托类实例,对实例方法做功能增强。

  saveUser 方法是代理类执行的逻辑,在方法内部有增强的代码逻辑,也保留了原始实例的代码功能。

测试代码:

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext();
        context.register(SpringConfig.class);
        context.refresh();
        UserService userService=context.getBean(UserService.class);
        UserServiceProxy proxy=new UserServiceProxy(userService);
        proxy.saveUser();
    }
}

运行结果
Spring——面向切面编程(AOP)_第1张图片
  可以看到,执行结果中即包含了被代理对象的原始保存方法的逻辑,也有代理类中对原始方法的两个增强代码。

3.3 动态代理示例

  创建动态处理器

public class DynamicProxy implements InvocationHandler {
    private Object object;

    public DynamicProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行前逻辑");
        Object result = method.invoke(object, args);
        System.out.println("执行后逻辑");
        return result;
    }
}

  测试类代码

public class test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(SpringConfig.class);
        context.refresh();
        // 获取接口实例
        UserService userService = context.getBean(UserService.class);
        // 动态创建实例的代码
        UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
                new Class[]{UserService.class}, new DynamicProxy(userService));
        // proxy执行方法
        proxy.saveUser();
    }
}

代码解析
  Proxy.newProxyInstance 是 JDK 提供的一个用于动态创建代理实例的方法,参数解释如下:
  ClassLoader loader: 指定当前目标对象使用的类加载器,获取加载器的方法是固定的。
  Class[] interfaces: 指定目标对象实现的接口的类型,使用泛型方式确认类型。
InvocationHandler: 指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法。

运行结果
Spring——面向切面编程(AOP)_第2张图片

4 Spring AOP 实现之 XML 配置

4.1 工程搭建介绍

数据库建表 SQL:

CREATE TABLE `account` (
  `id` int(11) NOT NULL auto_increment COMMENT 'id',
  `accountNum` varchar(20) default NULL COMMENT '账号',
  `money` int(8) default NULL COMMENT '余额',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

工程代码介绍
  实体类: 跟数据库表对应的 Java 类 Account ;
  操作实体类的: Dao 和 Dao 的接口实现类 ;
  调用持久层的业务类: Service 和 Service 的实现类 ;
  事务管理器类: TransactionManager 提供事务的一系列操作 ;
  测试代码类: 初始化 Spring 调用类中的方法测试 。

AOP 中的核心概念
Spring——面向切面编程(AOP)_第3张图片
  所以:在对原始业务类中的方法执行之前的增强行为就是前置通知,在对原始业务类中的方法执行之后的增强行为就是后置通知。而一旦出现异常,那么所做的动作就是异常通知。本案例使用几种通知,来实现事务的控制。

4.2 代码实现

创建 maven 工程:省略

pom 文件的依赖坐标如下:

     <dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.3.17version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-testartifactId>
            <version>5.3.17version>
        dependency>
        <dependency>
            <groupId>commons-dbutilsgroupId>
            <artifactId>commons-dbutilsartifactId>
            <version>1.7version>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.28version>
        dependency>
        <dependency>
            <groupId>c3p0groupId>
            <artifactId>c3p0artifactId>
            <version>0.9.1.2version>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.13.2version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.9.4version>
        dependency>
    dependencies>

实体类 Account

public class Account implements Serializable {
    //数据id
    private Integer id;
    //账号编码
    private String accountNum;
    //账号金额
    private Float money;

    //省略get 和set 方法
}

数据库连接工具类

public class ConnectionUtils {

    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 获取当前线程上的连接
     * @return
     */
    public Connection getThreadConnection() {
        try{
            //1.先从ThreadLocal上获取
            Connection conn = tl.get();
            //2.判断当前线程上是否有连接
            if (conn == null) {
                //3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 把连接和线程解绑
     */
    public void removeConnection(){
        tl.remove();
    }
}

持久层 dao 和 dao 的 实现类:

//dao的接口
public interface IAccountDao {
    /**
     * 更新
     * @param account
     */
    void updateAccount(Account account);
    /**
     * 根据编号查询账户
     */
    Account findAccountByNum(String accountNum);
}
//dao的实现类
public class AccountDaoImpl implements IAccountDao {
    //dbutil的查询工具类
    private QueryRunner runner;
    //连接的工具类
    private ConnectionUtils connectionUtils;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
   
    //修改账号
    public void updateAccount(Account account) {
        try{
            runner.update(connectionUtils.getThreadConnection(),"update account set accountNum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //根据账号查询
    public Account findAccountByNum(String accountNum) {
        try{
            List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where accountNum = ? ",new BeanListHandler<Account>(Account.class),accountNum);
            if(accounts == null || accounts.size() == 0){
                return null;
            }
            if(accounts.size() > 1){
                throw new RuntimeException("结果集不唯一,数据有问题");
            }
            return accounts.get(0);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

业务类 Service 和 Service 的实现类

//业务接口
public interface IAccountService {
    /**
     * 转账
     * @param sourceAccount        转出账户名称
     * @param targetAccount        转入账户名称
     * @param money             转账金额
     */
    void transfer(String sourceAccount, String targetAccount, Integer money);

}
//业务实现类
public class AccountServiceImpl implements IAccountService {
	//持久层对象
    private IAccountDao accountDao;
    //省略 set 和 get 方法

    //转账的方法
    public void transfer(String sourceAccount, String targetAccount, Integer money) {
		//查询原始账户
        Account source = accountDao.findAccountByNum(sourceAccount);
        //查询目标账户
        Account target = accountDao.findAccountByNum(targetAccount);
        //原始账号减钱
        source.setMoney(source.getMoney()-money);
        //目标账号加钱
        target.setMoney(target.getMoney()+money);
        //更新原始账号
        accountDao.updateAccount(source);
        //更新目标账号
        accountDao.updateAccount(target);
        System.out.println("转账完毕");

    }
}

事务管理器类

package com.ty.utils;

/**
 * 和事务管理相关的工具类,它包含了,开启事务,提交事务,回滚事务和释放连接
 */
public class TransactionManager {

    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }
    /**
     * 开启事务
     */
    public  void beginTransaction(){
        try {
            System.out.println("开启事务");
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 提交事务
     */
    public  void commit(){
        try {
            System.out.println("提交事务");
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 回滚事务
     */
    public  void rollback(){
        try {
            System.out.println("回滚事务");
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 释放连接
     */
    public  void release(){
        try {
            System.out.println("释放连接");
            connectionUtils.getThreadConnection().close();//还回连接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

  代码解释:此工具类作为 Spring 使用 AOP 管理事务的通知类,里面的各个方法用于配置 Spring 的通知使用。为了测试效果,在每个通知方法内,我们输出打印了测试语句。

  配置文件中添加 AOP 的相关配置


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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">

    
    <bean id="accountService" class="com.ty.service.AccountServiceImpl">
        <property name="accountDao" ref="accountDao">property>
    bean>
    
    <bean id="accountDao" class="com.ty.dao.AccountDaoImpl">
        <property name="runner" ref="runner">property>
        <property name="connectionUtils" ref="connectionUtils">property>
    bean>
    
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">bean>
    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver">property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mysql">property>
        <property name="user" value="root">property>
        <property name="password" value="root">property>
    bean>
    
    <bean id="connectionUtils" class="com.ty.utils.ConnectionUtils">
        
        <property name="dataSource" ref="dataSource">property>
    bean>

    
    <bean id="txManager" class="com.ty.utils.TransactionManager">
        
        <property name="connectionUtils" ref="connectionUtils">property>
    bean>
    
    <aop:config>
        <aop:pointcut expression="execution ( * com.ty.service.*.*(..))" id="pc"/>
        <aop:aspect ref="txManager">
            <aop:before method="beginTransaction" pointcut-ref="pc"/>
            <aop:after-returning method="commit" pointcut-ref="pc"/>
            <aop:after method="release" pointcut-ref="pc"/>
            <aop:after-throwing method="rollback" pointcut-ref="pc"/>
        aop:aspect>
    aop:config>

beans>

配置文件说明:
  connectionUtils: 获取数据库连接的工具类;
  dataSource: 采用 c3p0 数据源,要注意修改成自己数据库的名称与账号名和密码;
  queryRunner: dbutils 第三方框架提供用于执行 SQL 语句,操作数据库的一个工具类;
  accountDao 和 accountService:自定义的业务层实现类和持久层实现类;
  aop:config: 此节点是新增加 AOP 配置,AOP 相关信息都在这;
  aop:pointcut: 此节点是切入点,表示哪些类的哪些方法在执行的时候会应用 Spring 配置的通知进行增强;
  aop:aspect: 此节点是配置切面类的节点,它的作用主要就是整合通知和切入点。
  null 前置、后置、异常、和最终。可以看得出来 before 前置通知执行的方法是开启事务, after-returning 成功执行的方法是提交事务,after 最终执行的方法是释放连接,after-throwing 出现异常执行的方法是回滚。

测试类代码

import com.ty.service.IAccountService;
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;

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

    @Autowired
    private IAccountService accountService;

    @Test
    public  void testTransfer(){
        accountService.transfer("1","2",100);
    }
}

注意:将transfer方法的参数修改为自己数据库中对应的accountNum,比如我这里的是1和2。
Spring——面向切面编程(AOP)_第4张图片

运行结果:
Spring——面向切面编程(AOP)_第5张图片

  可以看到,我们通过在 xml 文件中配置 Spring 的 AOP 相关配置,就可以实现对业务类中的方法实现了增强,无需自定义对业务类做代理实现。

5 Spring AOP 实现之注解配置

5.1 工程搭建介绍

数据库建表 SQL:

CREATE TABLE `account` (
  `id` int(11) NOT NULL auto_increment COMMENT 'id',
  `accountNum` varchar(20) default NULL COMMENT '账号',
  `money` int(8) default NULL COMMENT '余额',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

工程代码介绍
  实体类: 跟数据库表对应的 Java 类 Account;
  操作实体类的: Dao 和 Dao 的接口实现类;
  调用持久层的业务类: Service 和 Service 的实现类;
  事务管理器类: TransactionManager 提供事务的一系列操作;
  测试代码类: 初始化 Spring 调用类中的方法测试。

使用注解介绍
  @Aspect:用于表明某个类为切面类,而切面类用于整合切入点和通知;
  @Pointcut:用于声明一个切入点,表明哪些类的哪些方法需要被增强;
  @Before:表示一个前置通知。即在业务方法执行之前做的事情;
  @AfterReturning:表示一个后置通知。即在业务方法执行之后做的事情;
  @After:表示一个后置通知。即在业务方法执行之后做的事情;
  @AfterThrowing:表示一个异常通知。即在业务代码执行过程中出现异常时做的事情。

5.2 代码实现

创建 maven 工程:省略

1.pom 文件的依赖坐标如下:

<dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.3.17version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-testartifactId>
            <version>5.3.17version>
        dependency>
        <dependency>
            <groupId>commons-dbutilsgroupId>
            <artifactId>commons-dbutilsartifactId>
            <version>1.7version>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.28version>
        dependency>
        <dependency>
            <groupId>c3p0groupId>
            <artifactId>c3p0artifactId>
            <version>0.9.1.2version>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.13.2version>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.9.4version>
        dependency>
    dependencies>

2.实体类 Account

public class Account implements Serializable {
    //数据id
    private Integer id;
    //账号编码
    private String accountNum;
    //账号金额
    private Float money;

    //省略get 和set 方法
}

3.数据库连接工具类

@Component
public class ConnectionUtils {

    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();

@Autowired
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 获取当前线程上的连接
     * @return
     */
    public Connection getThreadConnection() {
        try{
            //1.先从ThreadLocal上获取
            Connection conn = tl.get();
            //2.判断当前线程上是否有连接
            if (conn == null) {
                //3.从数据源中获取一个连接,并且存入ThreadLocal中
                conn = dataSource.getConnection();
                tl.set(conn);
            }
            //4.返回当前线程上的连接
            return conn;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    /**
     * 把连接和线程解绑
     */
    public void removeConnection(){
        tl.remove();
    }
}

4.持久层 dao 和 dao 的 实现类:

//dao的接口
public interface IAccountDao {
    /**
     * 更新
     * @param account
     */
    void updateAccount(Account account);
    /**
     * 根据编号查询账户
     */
    Account findAccountByNum(String accountNum);
}

//dao的实现类
@Repository
public class AccountDaoImpl implements IAccountDao {
    //dbutil的查询工具类
    @Autowired
    private QueryRunner runner;
    //连接的工具类
    @Autowired
    private ConnectionUtils connectionUtils;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    //修改账号
    public void updateAccount(Account account) {
        try{
            runner.update(connectionUtils.getThreadConnection(),"update account set accountNum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    //根据账号查询
    public Account findAccountByNum(String accountNum) {
        try{
            List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where accountNum = ? ",new BeanListHandler<Account>(Account.class),accountNum);
            if(accounts == null || accounts.size() == 0){
                return null;
            }
            if(accounts.size() > 1){
                throw new RuntimeException("结果集不唯一,数据有问题");
            }
            return accounts.get(0);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

  代码解释: AccountDaoImpl 类上的注解 @Repository 表示使用注解实例化此类,并交给 Spring 的容器管理。

5.业务类 Service 和 Service 的实现类:

//业务接口
public interface IAccountService {
    /**
     * 转账
     * @param sourceAccount        转出账户名称
     * @param targetAccount        转入账户名称
     * @param money             转账金额
     */
    void transfer(String sourceAccount, String targetAccount, Integer money);

}
//业务实现类
@Service
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;

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

    public void transfer(String sourceAccount, String targetAccount, Integer money) {

        Account source = accountDao.findAccountByNum(sourceAccount);
        Account target = accountDao.findAccountByNum(targetAccount);
        source.setMoney(source.getMoney()-money);
        target.setMoney(target.getMoney()+money);
        accountDao.updateAccount(source);
        accountDao.updateAccount(target);
        System.out.println("转账完毕");

    }
}

  代码解释:AccountServiceImpl 类上的注解 @Service 表示使用注解实例化此类,并交给 Spring 的容器管理。

6.事务管理器类

@Component
@Aspect
public class TransactionManager {
    @Autowired
    private ConnectionUtils connectionUtils;

    public void setConnectionUtils(ConnectionUtils connectionUtils) {
        this.connectionUtils = connectionUtils;
    }

    @Pointcut("execution(* com.offcn.service.impl.*.*(..))")
    private void pt1() {}

    /**
     * 开启事务
     */
    @Before("pt1()")
    public  void beginTransaction(){
        try {
            System.out.println("开启事务");
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 提交事务
     */
    @AfterReturning("pt1()")
    public  void commit(){
        try {
            System.out.println("提交事务");
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 回滚事务
     */
    @AfterThrowing("pt1()")
    public  void rollback(){
        try {
            System.out.println("回滚事务");
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 释放连接
     */
    @After("pt1()")
    public  void release(){
        try {
            System.out.println("释放连接");
            connectionUtils.getThreadConnection().close();//还回连接池中
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

代码解释
  此类通过注解 @Componet 实例化,并且交由 Spring 容器管理,@Aspect 表明它是一个切面类。

7.配置文件:


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/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">
   
    
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">bean>
    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver">property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mysql">property>
        <property name="user" value="root">property>
        <property name="password" value="root">property>
    bean>

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

配置文件说明
  dataSource: 采用 c3p0 数据源,注意修改成自己数据库的名称与账号名和密码;
  queryRunner: dbutils 第三方框架提供用于执行 sql 语句,操作数据库的一个工具类;
  context:component-scan:表示初始化容器扫描的包路径;
  aop:aspectj-autoproxy:表示开启代理模式。

8.测试类代码

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

    @Autowired
    private IAccountService accountService;

    @Test
    public  void testTransfer(){
        accountService.transfer("622200009999","622200001111",100);
    }
}

运行结果:
Spring——面向切面编程(AOP)_第6张图片
  可以看到,通过注解方式配置 Spring 的 AOP 相关配置,同样可以实现对数据的操作。

ps:以上内容来自对慕课教程的学习与总结

你可能感兴趣的:(Java开发,spring,java,aop,面向切面编程)