SpringAOP技术

目录

一、概念引入

1.引入依赖

2.工具类

3.实体类

4.持久层实现类

5.业务层实现类

6.配置文件

7.测试类

8.运行

查看数据库:

9.现在如果转账过程中出现异常

AccountServiceImpl(模拟异常)

再运行:

查看数据库: 

10.现在做事务管理

AccountServiceImpl(事务管理)

运行

查看数据库:

11.生成代理对象

代理对象:

业务层实现类

查看数据库:

二、AOP的概述

1.什么是AOP的技术

2.AOP的优势

3.AOP的底层原理

三、Spring的AOP技术-配置文件方式

1.引入依赖

2.spring的配置文件

3.写具体的接口和实现类

4.将目标类配置到spring中

5.定义切面类

6.在配置文件中定义切面类

7.在配置文件中完成AOP的配置

8.测试类

9.运行结果

四、用AOP实现事务管理(转账) 

1.实体类

2.工具类

3.业务层

4.持久层

5.切面类

6.配置文件

7.测试类

8.运行

9.查看数据库

10.现在模拟异常

查看数据库:​编辑

11.切面类中加一个环绕通知的方法

切面类

配置文件

五、Spring的AOP技术--注解方式

1.半注解

(1)业务层

(2)切面类

(3)配置文件中开启自动代理,扫描包

(4)测试类

(5)运行

(6)通知类型的注解

2.纯注解

(1)配置类

(2)测试类



一、概念引入

入门案例(概念引入)

1.引入依赖

<dependencies>
    <!--spring的核心依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <!--日志-->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <!--测试-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--连接池 alibaba的-->
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--mysql驱动-->
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>
    <!--spring-test-->
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>

2.工具类

package com.qcby.utils;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.alibaba.druid.pool.DruidDataSource;

/**
 * 事务的工具类
 */
public class TxUtils {
   
   private static DruidDataSource ds = null;

   // 使用ThreadLocal存储当前线程中的Connection对象
   private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();

   // 在静态代码块中创建数据库连接池---static代码块
   static {
      try {
         // 通过代码创建C3P0数据库连接池
         ds = new DruidDataSource();
         ds.setDriverClassName("com.mysql.jdbc.Driver");
         ds.setUrl("jdbc:mysql:///spring_db");
         ds.setUsername("root");
         ds.setPassword("2020");
      } catch (Exception e) {
         throw new ExceptionInInitializerError(e);
      }
   }

   /**
    * @Method: getConnection
    * @Description: 从数据源中获取数据库连接
    * @Anthor:
    * @return Connection
    * @throws SQLException
    */
   public static Connection getConnection() throws SQLException {

      // 从当前线程中获取Connection
      Connection conn = threadLocal.get();

      if (conn == null) {
         // 从数据源中获取数据库连接
         conn = getDataSource().getConnection();
         // 将conn绑定到当前线程
         threadLocal.set(conn);
      }
      return conn;
   }

   /**
    * @Method: startTransaction
    * @Description: 开启事务
    * @Anthor:
    *
    */
   //jdbc开启事务靠链接开启
   public static void startTransaction() {
      try {
         Connection conn = threadLocal.get();
         if (conn == null) {
            conn = getConnection();
            // 把 conn绑定到当前线程上
            threadLocal.set(conn);
         }

         // 开启事务
         conn.setAutoCommit(false);

      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Method: rollback
    * @Description:回滚事务
    * @Anthor:
    */
   public static void rollback() {
      try {
         // 从当前线程中获取Connection
         Connection conn = threadLocal.get();
         if (conn != null) {
            // 回滚事务
            conn.rollback();
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Method: commit
    * @Description:提交事务
    * @Anthor:
    */
   public static void commit() {
      try {
         // 从当前线程中获取Connection
         Connection conn = threadLocal.get();
         if (conn != null) {

            // 提交事务
            conn.commit();

         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Method: close
    * @Description:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池)
    * @Anthor:
    *
    */
   public static void close() {
      try {
         // 从当前线程中获取Connection
         Connection conn = threadLocal.get();//从连接池拿到链接
         if (conn != null) {
            conn.close();
            // 解除当前线程上绑定conn
            threadLocal.remove();
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Method: getDataSource
    * @Description: 获取数据源
    * @Anthor:
    * @return DataSource
    */
   public static DataSource getDataSource() {
      // 从数据源中获取数据库连接
      return ds;
   }

}

3.实体类

package com.qcby.model;

import java.io.Serializable;

public class Account implements Serializable {
    private Integer id;
    private String name;
    private Double money;

    public Account() {
    }

    public Account(Integer id, String name, Double money) {
        this.id = id;
        this.name = name;
        this.money = 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.持久层实现类

package com.qcby.dao.impl;

import com.qcby.dao.AccountDao;
import com.qcby.pojo.Account;
import com.qcby.utils.TxUtils;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import com.mysql.jdbc.Driver;

public class AccountDaoImpl implements AccountDao {
    //实现两个账户的转账
    @Override
    public void updateSaveAll(Account account){

        Connection connection=null;
        PreparedStatement stmt=null;
        try{
            //获取连接
            connection = TxUtils.getConnection();
            String sql = "update  account set money = money + ? where name = ?";
            stmt = connection.prepareStatement(sql);
            stmt.setDouble(1, account.getMoney());
            stmt.setString(2, account.getName());
            // 查询
            int result = stmt.executeUpdate();
            System.out.println("修改影响了"+result+"行数据!!");
        }catch(Exception e){
            try {
                stmt.close();
                //connection.close();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }
    }
}

5.业务层实现类

package com.qcby.service.impl;

import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.service.AccountService;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

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

    @Override
    public void updateSaveAll(Account account1, Account account2) {
        //保存账号1
        accountDao.updateSaveAll(account1);
        //保存账号2
        accountDao.updateSaveAll(account2);
    }
}

6.配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">



    <!--配置持久层和业务层-->
    <bean id="accountService" class="com.qcby.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <bean id="accountDao" class="com.qcby.dao.impl.AccountDaoImpl">
    </bean>

</beans>

7.测试类

package com.qcby;

import com.qcby.model.Account;
import com.qcby.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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class demo {

    @Autowired
    private AccountService accountService;

    @Test
    public void run(){
        Account account1=new Account(null,"aaa",500.00);
        Account account2=new Account(null,"bbb",-500.00);
        accountService.updateSaveAll(account1,account2);
    }
}

8.运行

查看数据库:

之前:

现在:

9.现在如果转账过程中出现异常

AccountServiceImpl(模拟异常)

package com.qcby.service.impl;

import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.service.AccountService;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

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

    @Override
    public void updateSaveAll(Account account1, Account account2) {
        //保存账号1
        accountDao.updateSaveAll(account1);
        //模拟异常
        int a=1/0;
        //保存账号2
        accountDao.updateSaveAll(account2);
    }
}

再运行:

查看数据库: 

只有第一条数据变了,出现错误

10.现在做事务管理

AccountServiceImpl(事务管理)

package com.qcby.service.impl;

import com.qcby.dao.AccountDao;
import com.qcby.model.Account;
import com.qcby.service.AccountService;
import com.qcby.utils.TxUtils;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

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

    @Override
    public void updateSaveAll(Account account1, Account account2) {
        //在这里进行事务管理
        try {
            //开启事务
            TxUtils.startTransaction();
            //保存账号1
            accountDao.updateSaveAll(account1);
            //模拟异常
            int a=1/0;
            //保存账号2
            accountDao.updateSaveAll(account2);
            //提交事务
            TxUtils.commit();
        }catch (Exception e){
            e.printStackTrace();
            //回滚事务
            TxUtils.rollback();
        }finally {
            //关闭资源
            TxUtils.close();
        }
    }
}

这里仍然有异常,但是进行了事务管理,

运行

查看数据库:

原来:

 现在:

 回滚了

11.生成代理对象

代理对象:

package com.qcby.proxy;

import com.qcby.service.AccountService;
import com.qcby.utils.TxUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*
* 传入目标对象,生成该对象的代理对象,返回。对目标对象的方法进行增强
* */
public class JdkProxy {
    //获取代理对象
    public static Object getProxy(AccountService accountService){
        /*
        * 使用JDK动态代理生成代理对象
        * 第一个参数:类的加载其
        * 第二个参数:当前传入的对象实现了哪些接口要字节码的对象
        * 第三个参数:回调函数
        * */
        Object proxy = Proxy.newProxyInstance(JdkProxy.class.getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler(){
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                try {
                    //开启事务
                    TxUtils.startTransaction();
                    result = method.invoke(accountService,args);
                    //提交事务
                    TxUtils.commit();
                }catch (Exception e){
                    e.printStackTrace();
                    //回滚
                    TxUtils.rollback();
                }finally {
                    TxUtils.close();
                }
                return result;
            }
        });
        return proxy;
    }
}

业务层实现类

(有异常)(之前在这里写的事务就不用写了)

package com.qcby.service.impl;

import com.qcby.dao.AccountDao;
import com.qcby.pojo.Account;
import com.qcby.service.AccountService;
import com.qcby.utils.TxUtils;

import java.sql.SQLException;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;//因为这里是空指针,所以要有set方法

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

    public void updateSaveAll(Account account1, Account account2){
        try {
            //保存账号1
            accountDao.updateSaveAll(account1);
            //模拟异常
            int a=1/0;
            //保存账号2
            accountDao.updateSaveAll(account2);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

测试类

(多了一个run2())

package com.qcby.springAopTest;

import com.qcby.pojo.Account;
import com.qcby.proxy.JdkProxy;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void run1(){
        Account account1=new Account(null,"aaa",500.00);
        Account account2=new Account(null,"bbb",-500.00);
        accountService.updateSaveAll(account1,account2);
    }

    @Test
    public void run2(){
        Account account1=new Account(null,"aaa",500.00);
        Account account2=new Account(null,"bbb",-500.00);
        //生成代理对象
        Object proxyobj= JdkProxy.getProxy(accountService);
        AccountService proxy=(AccountService)proxyobj;
        proxy.updateSaveAll(account1,account2);
    }
}

运行:

查看数据库:

原来:

现在:

二、AOP的概述

1.什么是AOP的技术

AOP是Aspect Oriented Programming的缩写,意为:面向切面编程,它是通过预编译的方式或者运行期动态代理实现程序功能的统一维护的一种技术

2.AOP的优势

可以在不修改源代码的情况下对已有方法进行增强

(1)减少重复代码

(2)提高开发效率

(3)维护方便

3.AOP的底层原理

JDK的动态代理

三、Spring的AOP技术-配置文件方式

1.引入依赖

<dependencies>
    <!--spring的核心依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <!--日志-->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.3</version>
    </dependency>
</dependencies>

2.spring的配置文件

引入具体的AOP的schema约束

<?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"
       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">


</beans>

3.写具体的接口和实现类

接口

package com.qcby.service;

public interface UserService {
    public void save();
}

实现类

package com.qcby.service.impl;

import com.qcby.service.UserService;

/*
* 业务层实现类
* 实现AOP切面增强
* */
public class UserServiceImpl implements UserService {
    //目标对象目标方法
    @Override
    public void save() {
        System.out.println("业务层:保存用户....");
    }
}

4.将目标类配置到spring中

<bean id="userService" class="com.qcby.service.impl.UserServiceImpl"></bean>

5.定义切面类

现在他就是一个普通的类,里面有一个普通的方法,所以要在配置文件里配置

/*
* 自定义切面类 = 切入点(表达式) + 通知(增强的代码)
* */
public class MyXmlAspect {
    //发送手机短信...
    public void log(){
        System.out.println("增强的方法执行了....");
    }
}

6.在配置文件中定义切面类

这时候是一个被spring管理的类,这句话只是把它交给spring去管理

<bean id="myAspect" class="com.qcby.aspect.MyXmlAspect"></bean>

7.在配置文件中完成AOP的配置

<!--配置AOP的增强-->
<aop:config>
    <!--配置切面=切入点+通知 组成--><!-- 到这一步代表MyXmlAspect这个类已经是是一个切面类了,但是还没有通知 -->
    <aop:aspect ref="myXmlAspect">
        <!--前置通知:userServiceImpl的save方法执行前会增强--> <!-- 这里是对方法进行增强 -->
        <aop:before method="log" pointcut="execution(public void com.qcby.service.impl.UserServiceImpl.save())"/>
        <!--后置通知-->
        <aop:after-returning method="log" pointcut="execution(public void com.qcby.service.impl.UserServiceImpl.save*(..))"/>
        <!--异常-->
        <aop:after-throwing method="log" pointcut="execution(public void com.qcby.service.impl.UserServiceImpl.save*(..))"/>
    </aop:aspect>
</aop:config>
切入点表达式(前置):(这个是只对save()方法进行增强,如果有一个save1()方法,最后的运行结果只会输出业务层:保护用户……)
pointcut="execution(public void com.qcby.service.impl.UserServiceImpl.save())"
<!--后置通知-->
<aop:after-returning method="log" pointcut="execution(public void com.qcby.service.impl.UserServiceImpl.save*(..))"/>
<!--异常通知-->
<aop:after-throwing method="log" pointcut="execution(* com.qcby.*.*.*ServiceImpl.save*(..))"/>
表达式: public可以不写,
        void这里必须写,可以写*
        包名+类名  不能省略不写,可以写*  UserserviceImpl AccountserviceImpl
        方法名  save() 可以写*
        参数列表   (..) 表示任意类型和个数的参数
比较通用的表达式:execution(public * cn.tx.*.*ServiceImpl.*(..))

8.测试类

import com.qcby.service.UserService;
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("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private UserService userService;
    @Test
    public void run1(){
        userService.save();
    }
}

9.运行结果

前置和后置都配置了,所以开始输出一次  增强的方法执行了...  ,最后再输出一次

四、用AOP实现事务管理(转账) 

在切面类里写四个方法(开启、提交、关闭、回滚)

开始的时候开启事务,完成提交事务,最后的时候关闭资源,有异常回滚

1.实体类

import java.io.Serializable;

public class Account implements Serializable {
    private Integer id;
    private String name;
    private Double money;

    public Account() {
    }

    public Account(Integer id, String name, Double money) {
        this.id = id;
        this.name = name;
        this.money = 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 +
                '}';
    }
}

2.工具类

import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * 事务的工具类
 */
public class TxUtils {
   
   private static DruidDataSource ds = null;

   // 使用ThreadLocal存储当前线程中的Connection对象
   private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();

   // 在静态代码块中创建数据库连接池---static代码块
   static {
      try {
         // 通过代码创建C3P0数据库连接池
         ds = new DruidDataSource();
         ds.setDriverClassName("com.mysql.jdbc.Driver");
         ds.setUrl("jdbc:mysql:///spring_db");
         ds.setUsername("root");
         ds.setPassword("2020");
      } catch (Exception e) {
         throw new ExceptionInInitializerError(e);
      }
   }

   /**
    * @Method: getConnection
    * @Description: 从数据源中获取数据库连接
    * @Anthor:
    * @return Connection
    * @throws SQLException
    */
   public static Connection getConnection() throws SQLException {

      // 从当前线程中获取Connection
      Connection conn = threadLocal.get();

      if (conn == null) {
         // 从数据源中获取数据库连接
         conn = getDataSource().getConnection();
         // 将conn绑定到当前线程
         threadLocal.set(conn);
      }
      return conn;
   }

   /**
    * @Method: startTransaction
    * @Description: 开启事务
    * @Anthor:
    *
    */
   //jdbc开启事务靠链接开启
   public static void startTransaction() {
      try {
         Connection conn = threadLocal.get();
         if (conn == null) {
            conn = getConnection();
            // 把 conn绑定到当前线程上
            threadLocal.set(conn);
         }

         // 开启事务
         conn.setAutoCommit(false);

      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Method: rollback
    * @Description:回滚事务
    * @Anthor:
    */
   public static void rollback() {
      try {
         // 从当前线程中获取Connection
         Connection conn = threadLocal.get();
         if (conn != null) {
            // 回滚事务
            conn.rollback();
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Method: commit
    * @Description:提交事务
    * @Anthor:
    */
   public static void commit() {
      try {
         // 从当前线程中获取Connection
         Connection conn = threadLocal.get();
         if (conn != null) {

            // 提交事务
            conn.commit();

         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Method: close
    * @Description:关闭数据库连接(注意,并不是真的关闭,而是把连接还给数据库连接池)
    * @Anthor:
    *
    */
   public static void close() {
      try {
         // 从当前线程中获取Connection
         Connection conn = threadLocal.get();//从连接池拿到链接
         if (conn != null) {
            conn.close();
            // 解除当前线程上绑定conn
            threadLocal.remove();
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Method: getDataSource
    * @Description: 获取数据源
    * @Anthor:
    * @return DataSource
    */
   public static DataSource getDataSource() {
      // 从数据源中获取数据库连接
      return ds;
   }

}

3.业务层

import com.qcby.dao.AccountDao;
import com.qcby.pojo.Account;
import com.qcby.service.AccountService;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

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

    @Override
    public void updateSaveAll(Account account1, Account account2) {
        //账号1
        accountDao.updateSaveAll(account1);
        
        //模拟异常
        //int a=1/0;

        //账号2
        accountDao.updateSaveAll(account2);
    }
}

4.持久层

import com.qcby.dao.AccountDao;
import com.qcby.pojo.Account;
import com.qcby.utils.TxUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class AccountDaoImpl implements AccountDao {
    @Override
    public void updateSaveAll(Account account) {
        Connection connection = null;
        PreparedStatement stmt=null;
        try {
            connection=TxUtils.getConnection();
            String sql="update  account set money = money + ? where name = ?";
            stmt=connection.prepareStatement(sql);
            stmt.setDouble(1,account.getMoney());
            stmt.setString(2,account.getName());
            int num=stmt.executeUpdate();
            System.out.println("修改了"+num+"条数据");
        } catch (SQLException e) {
            try {
                stmt.close();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
    }
}

5.切面类

import com.qcby.utils.TxUtils;

public class MyAspect {

    public void before(){
        System.out.println("转账之前开启事务");
        TxUtils.startTransaction();
    }

    public void afterThrowing(){
        System.out.println("转账异常,回滚事务");
        TxUtils.rollback();
    }

    public void after(){
        System.out.println("转账之后,关闭资源");
        TxUtils.close();
    }

    public void afterReturning(){
       System.out.println("转账成功,提交事务");
        TxUtils.commit();
    }
    
}J

6.配置文件

<?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"
       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">

    <bean id="accountService" class="com.qcby.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <bean id="accountDao" class="com.qcby.dao.impl.AccountDaoImpl"/>

    <bean id="myAspect" class="com.qcby.aspect.MyAspect"/>
    <!--配置AOP-->
    <aop:config>
        <aop:aspect ref="myAspect">
            <aop:before method="before" pointcut="execution(* com.qcby.service.impl.AccountServiceImpl.updateSaveAll(..))"></aop:before>
            <aop:after-throwing method="afterThrowing" pointcut="execution(* com.qcby.service.impl.AccountServiceImpl.updateSaveAll(..))"></aop:after-throwing>
            <aop:after-returning method="afterReturning" pointcut="execution(* com.qcby.service.impl.AccountServiceImpl.updateSaveAll(..))"></aop:after-returning>
            <aop:after method="after" pointcut="execution(* com.qcby.service.impl.AccountServiceImpl.updateSaveAll(..))"></aop:after>
        </aop:aspect>
    </aop:config>


</beans>

7.测试类

import com.qcby.pojo.Account;
import com.qcby.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;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void run(){
        Account account1=new Account(1,"aaa",500.0);
        Account account2=new Account(2,"bbb",-500.0);
        accountService.updateSaveAll(account1,account2);
    }
}

8.运行

9.查看数据库

(原来是1500  1500)

10.现在模拟异常

查看数据库:

回滚了,数据没变

11.切面类中加一个环绕通知的方法

环绕通知包含所有的通知

切面类

/**
 * 环绕通知
 * 问题:目标对象的方法没有执行,需要手动执行目标对象的方法。
 */
public void aroundLog(ProceedingJoinPoint point){

    try {
        System.out.println("增强的方法执行了...");
        TxUtils.startTransaction();
        // 让目标对象的方法去执行
        point.proceed();
        System.out.println("提交增强");
        TxUtils.commit();
    } catch (Throwable throwable) {
        throwable.printStackTrace();
        System.out.println("异常增强");
        TxUtils.rollback();
    }finally {
        System.out.println("关闭增强");
        TxUtils.close();
    }

}

配置文件

配置AOP那部分改成下面这个

<!--配置AOP-->
<aop:config>
    <!--配置切面-->
    <aop:aspect ref="myAspect">
        <aop:around method="aroundLog" pointcut="execution(* com.qcby.service.impl.AccountServiceImpl.updateSaveAll(..))"></aop:around>
    </aop:aspect>
</aop:config>

五、Spring的AOP技术--注解方式

1.半注解

配置文件中只有开启自动代理和扫描包

(1)业务层

import com.qcby.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("业务层:保存用户....");
    }
}

(2)切面类

@Component //把该类交给IOC去管理
@Aspect //声明是切面类 == <aop:aspect ref="myXmlAspect">
public class MyXmlAspect {
    //发送手机短信...
    @Before(value = "execution(public void com.qcby.service.impl.UserServiceImpl.save(..))")//切面的表达式
    public void log(){
        System.out.println("增强的方法执行了....");
    }
}

(3)配置文件中开启自动代理扫描包

<?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"
       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.qcby"></context:component-scan>
    <!--开启自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>


</beans>

(4)测试类

import com.qcby.service.UserService;
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("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private UserService userService;
    @Test
    public void run1(){
        userService.save();
    }
}

(5)运行

(6)通知类型的注解

 @Before -- 前置通知

​ @AfterReturing -- 后置通知

​ @Around -- 环绕通知

​ @After -- 最终通知

​ @AfterThrowing -- 异常抛出通知

2.纯注解

把配置文件换成了配置类,业务层、切面类和半注解的相同

(1)配置类

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

@Configuration //配置类
@ComponentScan(value = "com.qcby.service.impl") //扫描包结构
@EnableAspectJAutoProxy //开启自动代理 == <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
public class SpringConfig {
}

(2)测试类

import com.qcby.config.SpringConfig;
import com.qcby.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class AopTest2 {

    @Autowired
    private UserService userService;
    @Test
    public void run(){
        userService.save();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值