基于XML的AOP开发

需求:使用AOP对转账案例进行优化,业务层仅保留核心业务,事务的控制使用AOP来完成

步骤分析

1. 新建maven项目,创建以下包类

2. 导入依赖(pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring_aop_xml</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <!-- Spring的AOP开发依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <!-- java单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
        <!-- 连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.7</version>
        </dependency>

    </dependencies>
</project>

3. pojo实体类、dao(impl)持久层、service(impl)业务层

package com.bjpowernode.pojo;
public class Account { private Integer id; private String name; private Double money; // 省略了 getter、setter、有参构造、无参构造、toString()方法 }
package com.bjpowernode.dao;
import com.bjpowernode.pojo.Account;
public interface AccountDao { Account get(int id); void updateMoney(String name,int money); }
package com.bjpowernode.dao.impl;
import com.bjpowernode.dao.AccountDao;
import com.bjpowernode.pojo.Account;
import com.bjpowernode.utils.JDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import java.sql.SQLException;

public class AccountDaoImpl implements AccountDao {
    private QueryRunner qr;
    public void setQr(QueryRunner qr) {  this.qr = qr;  }

    private JDBCUtils jdbcUtils;
    public void setJdbcUtils(JDBCUtils jdbcUtils) {  this.jdbcUtils = jdbcUtils;  }

    @Override
    public Account get(int id) {
        try {
            String sql = "select * from account where id=?";
            return qr.query(sql, new BeanHandler<>(Account.class), id);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateMoney(String name, int money) {
        String sql = "update account set money = money + ? where name = ?";
        try {
            qr.update(jdbcUtils.getConnection(),sql,money,name);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
package com.bjpowernode.service;
import com.bjpowernode.pojo.Account;
public interface AccountService { Account get(int id); void transfer(String fromUser,String toUser,int money); }
package com.bjpowernode.service.impl;
import com.bjpowernode.dao.AccountDao;
import com.bjpowernode.pojo.Account;
import com.bjpowernode.service.AccountService;

public class AccountServiceImpl implements AccountService {

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

    @Override
    public Account get(int id) {  return accountDao.get(id);  }

    @Override
    public void transfer(String fromUser, String toUser, int money) {

        accountDao.updateMoney(fromUser, -money);
        //int a = 1/0;
        accountDao.updateMoney(toUser, money);   
    }
}

4. utils工具类

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

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

    // ThreadLocal和当前线程相关的对象,可以将对象绑定到线程上,在同一个线程中,获取到的始终是同一个对象
    private static ThreadLocal<Connection> TL = new ThreadLocal<>();

    public Connection getConnection() {
        Connection conn = null;
        try {
            conn = TL.get(); // 从线程上获取同一连接对象
            if ( conn == null ) {
                // 当前线程第一次调用getConnection方法
                conn = dataSource.getConnection();
                // 将连接对象和线程进行绑定
                TL.set(conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        // System.out.println(conn);
        return conn;
    }


    public void begin() {
        try {
            // 自动连接?:否,改手动
            getConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void rollback() {
        try {
            getConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void commit() {
        try {
            getConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void release() {
        try {
            // 不是真正的关闭,而是将连接对象返回给连接池,底层是动态代理技术
            getConnection().close();
            // 将连接对象从当前线程移除
            TL.remove();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

5. 通知类(对目标方法进行增强的类)

package com.bjpowernode.advice;

import com.bjpowernode.utils.JDBCUtils;
import org.aspectj.lang.ProceedingJoinPoint;

import java.sql.SQLException;
// 事务管理器,对业务方法提供事务的增强处理
public class TransactionManager {
    private JDBCUtils jdbcUtils;
    public void setJdbcUtils(JDBCUtils jdbcUtils) {
        this.jdbcUtils = jdbcUtils;
    }

    public void begin() {
        System.out.println("开启事务");
        try {
            jdbcUtils.getConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void rollback() {
        System.out.println("回滚事务");
        try {
            jdbcUtils.getConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void commit() {
        System.out.println("提交事务");
        try {
            jdbcUtils.getConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 环绕通知:整合前面的通知
    public Object around(ProceedingJoinPoint pjp) {
        Object result = null;
        try {
            begin(); // 开启事务
            result = pjp.proceed(); // 执行目标方法
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            rollback(); // 事务回滚
        } finally {
            commit(); // 事务提交
            jdbcUtils.release(); // 释放
        }
        return result;
    }
}

6. 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"
       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/aop 
       https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启组件扫描-->
    <context:component-scan base-package="com.bjpowernode"/>

    <!--配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- set方法注入 -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/mydb" />
        <property name="username" value="root" />
        <property name="password" value="root"/>
    </bean>
<!--DBUtils的核心类:QueryRunner-->
    <bean id="qr" class="org.apache.commons.dbutils.QueryRunner">
        <!-- 构造方法注入 -->
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

<!-- JDBCUtils -->
    <bean id="jdbcutils" class="com.bjpowernode.utils.JDBCUtils">
        <property name="dataSource" ref="dataSource" />
    </bean>

<!-- 配置Dao、Service -->
    <bean id="accountDao" class="com.bjpowernode.dao.impl.AccountDaoImpl">
        <property name="qr" ref="qr" />
        <property name="jdbcUtils" ref="jdbcutils" />
    </bean>

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

<!-- AOP相关配置--> <!-- 1. 配置通知类(对业务方法提供增强的类)--> <bean id="transactionManager" class="com.bjpowernode.advice.TransactionManager" > <property name="jdbcUtils" ref="jdbcutils" /> </bean> <!-- 2. aop配置:使用哪个通知类对哪些方法进行增强 --> <!-- 方式一 <aop:config> <aop:aspect ref="transactionManager"> &lt;!&ndash; pointcut: 切入点,即目标方法 &ndash;&gt; &lt;!&ndash; 前置通知:在切入点执行之前,先运行transactionManager实例中的begin方法 &ndash;&gt; <aop:before method="begin" pointcut="execution(public void com.bjpowernode.service.impl.AccountServiceImpl.transfer(String,String,int))" /> &lt;!&ndash; 发生异常时 &ndash;&gt; <aop:after-throwing method="rollback" pointcut="execution(public void com.bjpowernode.service.impl.AccountServiceImpl.transfer(String,String,int))" /> &lt;!&ndash; 最终通知 &ndash;&gt; <aop:after method="commit" pointcut="execution(public void com.bjpowernode.service.impl.AccountServiceImpl.transfer(String,String,int))" /> </aop:aspect> </aop:config> --> <!-- 方式二 <aop:config> &lt;!&ndash; pointcut: 切入点,即目标方法 &ndash;&gt; <aop:pointcut id="transfer" expression="execution(public void com.bjpowernode.service.impl.AccountServiceImpl.transfer(String,String,int))"/> &lt;!&ndash; 配置切面 ref: 使用哪个实例对切面(事务)进行增强 &ndash;&gt; <aop:aspect ref="transactionManager"> &lt;!&ndash; 前置通知:在切入点执行之前 &ndash;&gt; <aop:before method="begin" pointcut-ref="transfer" /> &lt;!&ndash; 发生异常时 &ndash;&gt; <aop:after-throwing method="rollback" pointcut-ref="transfer" /> &lt;!&ndash; 最终通知:在切入点执行之后 &ndash;&gt; <aop:after method="commit" pointcut-ref="transfer" /> </aop:aspect> </aop:config> --> <!-- 方式三:推荐!!! --> <aop:config> <!-- pointcut: 切入点,即目标方法 --> <aop:pointcut id="transfer" expression="execution(public void com.bjpowernode.service.impl.AccountServiceImpl.transfer(String,String,int))"/> <!-- 配置切面 ref: 使用哪个实例对切面(事务)进行增强 --> <aop:aspect ref="transactionManager"> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="transfer" /> </aop:aspect> </aop:config> </beans>

7. 测试类

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

@ContextConfiguration("classpath:applicationContext.xml") // 指定配置文件的所在路径
@RunWith(SpringJUnit4ClassRunner.class) // 指定使用spring编写的增强的junit类运行代码
public class Tester {
    @Autowired
    AccountService accountService;

    @Test
    public void testFindById(){
        System.out.println(accountService.get(1));
    }

    @Test
    public  void testUpdateMoney(){
        accountService.transfer("tom","jack",100); // tom向jack转移100
    }
}

 

posted @ 2022-11-21 17:22  鹿先森JIAN  阅读(13)  评论(0编辑  收藏  举报