java框架SSM学习——XML实现AOP操作

本文介绍如何在SSM框架中使用XML配置实现AOP事务管理。通过修改连接工具类、创建事务类并配置Spring,最终通过测试验证了事务管理的正确性,确保异常情况下数据库数据不会被修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在写之前先提醒一下,大部分代码还是沿用上一篇的,在其基础上更改。若没有案例代码的,可以去我文章的上一篇中查找。但是我在此需要对代码进行一部分的修改:①获取连接的方式②新增事务类

连接工具类ConnectionUitls

package com.utils;
import javax.sql.DataSource;
import java.sql.Connection;

//连接的工具类,用于从数据源中获取一个连接,并且实现和线程的绑定
public class ConnectionUtils {
    private ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();

    private DataSource dataSource;

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

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

    //把连接和当前线程解绑
    public void removeConnection(){
        threadLocal.remove();
    }
}

事务类TransactionManager

package com.itheima.utils;

//和事务管理相关的工具类
//开启事务、提交事务、回滚事务和释放连接
public class TransactionManager {
    private ConnectionUtils connectionUtils;

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

    //开启事务
    public void beginTransaction(){
        try {
            connectionUtils.getThreadConnection().setAutoCommit(false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //提交事务
    public void commitTransaction(){
        try{
            connectionUtils.getThreadConnection().commit();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //回滚事务
    public void rollbackTransaction(){
        try{
            connectionUtils.getThreadConnection().rollback();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //释放连接
    public void release(){
        try{
            //将连接放回连接池中
            connectionUtils.getThreadConnection().close();
            connectionUtils.removeConnection();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

现在有了事务类的这个需要增强的方法,我们还得对AccountDaoImpl进行获取连接的修改

package com.dao.impl;

import com.dao.IAccountDao;
import com.domain.Account;
import com.utils.ConnectionUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.util.List;

//账户的持久层实现类
public class AccountDaoImpl implements IAccountDao {

    private QueryRunner runner;

    private ConnectionUtils connectionUtils;

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

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

    public List<Account> findAllAccount() {
        try {
            return runner.query(connectionUtils.getThreadConnection(),"select * from account", new BeanListHandler<Account>(Account.class));
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public Account findAccountById(Integer id) {
        try {
            return runner.query(connectionUtils.getThreadConnection(),"select * from account where id = ?", new BeanHandler<Account>(Account.class) , id);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void saveAccount(Account account) {
        try {
            runner.update(connectionUtils.getThreadConnection(),"insert into account(name , money) values (? , ?)" , account.getName() , account.getMoney());
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try {
            runner.update(connectionUtils.getThreadConnection(),"update account set name = ? , money = ? where id = ?" , account.getName() , account.getMoney() , account.getId());
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public void deleteAccount(Integer id) {
        try {
            runner.update(connectionUtils.getThreadConnection(),"delete from account where id = ?" , id);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public Account findAccountByName(String accountName) {
        try {
            List<Account> accounts = runner.query(connectionUtils.getThreadConnection(),"select * from account where name = ? ", new BeanListHandler<Account>(Account.class) , accountName);
            if(accounts == null || accounts.size() == 0){
                return null;
            }
            if(accounts.size() > 1){
                throw new RuntimeException("结果集不为1,数据有问题");
            }
            return accounts.get(0);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

最后在Bean.xml配置中添加新建的ConnectionUtils对象以及TransactionManager对象

<!--配置ConnectionUtils-->
    <bean id="connectionUtils" class="com.itheima.utils.ConnectionUtils">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置事务管理器TransactionManager-->
    <bean id="transactionManager" class="com.itheima.utils.TransactionManager">
        <!--注入ConnectionUtils-->
        <property name="connectionUtils" ref="connectionUtils"></property>
    </bean>
事务管理器TransactionManager已经创建出来了,那需要什么配置去通知Spring我已经开启事务管理这个增强方法呢?
<aop:config></aop:config>
此元素标签代表这里是AOP配置,所有的AOP配置都写在里面。
<aop:aspect></aop:aspect>
此元素标签代表这里是切面,属性id代表的是此切面的唯一标识,属性ref代表的是切面的Bean对象配置引用
<aop:pointcut></aop:pointcut>
此元素标签代表切面表达式,属性id代表的是此切面表达式的唯一标识,属性expression里代表的是切面表达式
<aop:before></aop:before>
此元素标签代表的是前置通知。属性method代表的是在增强类中要使用到的方法的方法名,pointcut-ref代表的是引用<aop:pointcut>标签中定义的切面表达式
<aop:after-returning></aop:after-returning>
此元素标签代表的是后置通知。属性method代表的是在增强类中要使用到的方法的方法名,pointcut-ref代表的是引用<aop:pointcut>标签中定义的切面表达式
<aop:after-throwing></aop:after-throwing>
此元素标签代表的是异常通知。属性method代表的是在增强类中要使用到的方法的方法名,pointcut-ref代表的是引用<aop:pointcut>标签中定义的切面表达式
<aop:after></aop:after>
此元素标签代表的是最终通知。属性method代表的是在增强类中要使用到的方法的方法名,pointcut-ref代表的是引用<aop:pointcut>标签中定义的切面表达式

那么通过这些标签的使用就可以告诉Spring需要开启的AOP操作需要的通知。

<!--配置AOP-->
    <aop:config>
        <aop:aspect id="OpenTransactionManager" ref="transactionManager">
            <aop:pointcut id="transactionManagerExpression" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut>
            <aop:before method="beginTransaction" pointcut-ref="transactionManagerExpression"></aop:before>
            <aop:after-returning method="commitTransaction" pointcut-ref="transactionManagerExpression"></aop:after-returning>
            <aop:after-throwing method="rollbackTransaction" pointcut-ref="transactionManagerExpression"></aop:after-throwing>
            <aop:after method="release" pointcut-ref="transactionManagerExpression"></aop:after>
        </aop:aspect>
    </aop:config>

测试结果

数据库中数据
在这里插入图片描述
运行无异常的代码时:

@Test
    public void testTransfer(){
        as.transfer("aaa" , "bbb" , 200f);
    }

结果:

此结果可以证明代码可以运行并没有异常。

然后在其中加入之前的异常
            int i =1/0;

运行结果:
在这里插入图片描述
可以从中看到,出现了异常。那么数据库信息改变了吗?
在这里插入图片描述
答案是没有的,证明经过我们的配置,已经将事务管理器增强到了上篇没有的代码上了。这样,我们使用xml去实现AOP操作,就完成了。

细节

我们总结一下全部步骤:

	spring中基于XML的AOP配置步骤
        1.把通知的Bean也交给spring来管理
        2.使用aop:config标签表明开始AOP的配置
        3.使用aop:aspect标签表明配置切面
            id:给切面提供一个唯一标志
            ref:是指定通知类bean的Id
        4.在aop:aspect标签内部使用对应的标签来配置通知的类型
            我们现在的示例是让pringLog方法在切入点方法执行前执行,所以是前置执行
            aop:before:表示配置前置通知
                method:用于指定Logger类中哪个方法是前置通知
        5.pringcut属性:用于指定切入点表达式:该表达式的含义指的是对业务层中哪些方法增强
            切入点表达式的写法:
                关键字:execute(表达式)
                表达式:
                    访问修饰符  返回值   包名.包名....类名.方法名(参数列表)
                标准的表达式写法:
                    public void com.service.impl.AccountService.saveAccount()
                访问修饰符可以省略
                    void com.service.impl.AccountService.saveAccount()
                返回值可以使用通配符,表示任意返回值
                    * com.service.impl.AccountService.saveAccount()
                包名可以使用通配符,表示任意包,但是有几级包就要写几个*.
                    * *.*.*.*.AccountService.saveAccount()
                包名可以使用..表示当前包及其子包
                    * *..AccountService.saveAccount()
                类名和方法名都可以使用*实现通配
                    * *..*.*()
                参数列表可以直接写数据类型:
                    基本类型直接写名称            int
                    引用类型直接写包名.类名的方法  java.lang.String
                    可以使用通配符,表示任意类型,但是必须有参数
                    可以使用..表示有无参数均可,有参数可以是任意类型
                全通配写法
                    * *..*.*(..)

                实际开发中切入点表达式的通常写法:
                    切到业务层实现类下的所有方法
                       * * com.service.impl.*.*(..)

那么我们基于xml简单配置AOP就完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值