概述

本文主要介绍如何在上一节的业务基础上手动实现AOP(面向切面编程)。需要注意的是,这里的AOP还不具备扩展能力,只是一个简易的实现,帮助我们更好地实现对事务的控制。


AOP(面向切面编程)

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它通过分离关注点来提高代码的模块化,尤其是与横切关注点(cross-cutting concerns)相关的功能。这些横切关注点是指那些与业务逻辑并非直接相关,但却需要跨越多个模块的功能,如日志记录、安全验证、事务管理等。


AOP的核心概念

切面(Aspect)

切面是AOP中最重要的概念,它是对某个横切关注点的模块化。一个切面定义了一个与核心业务逻辑无关的功能(例如,日志记录、性能监控等),并把这些功能应用到程序的各个部分。

连接点(Joinpoint)

连接点指的是程序中能够插入切面的地方,通常是方法调用、方法执行、异常抛出等。

通知(Advice)

通知是切面在连接点上执行的具体行为或操作。通知有不同的类型:

  • 前置通知(Before):在目标方法执行前执行
  • 后置通知(After):在目标方法执行后执行,不管方法是成功还是失败
  • 环绕通知(Around):在目标方法执行前后都可以控制
  • 异常通知(Throws):在目标方法抛出异常后执行
  • 返回通知(AfterReturning):在目标方法正常返回后执行

切点(Pointcut)

切点是一个定义了通知执行位置的表达式,它指定了哪些连接点需要被拦截。

织入(Weaving)

织入是将切面应用到目标对象的过程。常见的织入方式有:

  • 编译时织入(例如使用AspectJ编译器)
  • 类加载时织入
  • 运行时织入(Spring AOP)

Resources配置

<?xml version="1.0" encoding="UTF-8" ?>
<!-- BeanFactory 类会进行处理这块内容 -->
<beans>
    <!-- WzkConnectionUtils 交给容器管理 -->
    <bean id="wzkConnectionUtils" class="wzk.utils.WzkConnectionUtils"></bean>

    <!-- 依赖了工具类 WzkConnectionUtils -->
    <!-- id 是放入到容器中的名称 -->
    <bean id="wzkAccountDao" class="wzk.dao.impl.JdbcWzkAccountDaoImpl">
        <!-- name是成员变量名字 ref是引用从容器中拿对象 -->
        <property name="WzkConnectionUtils" ref="wzkConnectionUtils"/>
    </bean>

    <!-- 依赖了 WzkAccountDao -->
    <bean id="wzkTransferService" class="wzk.service.impl.WzkTransferServiceImpl">
        <!-- name是成员变量名字 ref是引用从容器中拿对象 -->
        <property name="WzkAccountDao" ref="wzkAccountDao"></property>
    </bean>

    <!-- WzkTransactionManager 依赖了 WzkConnectionUtils -->
    <bean id="wzkTransactionManager" class="wzk.utils.WzkTransactionManager">
        <property name="WzkConnectionUtils" ref="wzkConnectionUtils"></property>
    </bean>

    <!-- ProxyFactory 依赖了 WzkTransactionManager -->
    <bean id="proxyFactory" class="wzk.factory.ProxyFactory">
        <property name="WzkTransactionManager" ref="wzkTransactionManager"></property>
    </bean>
</beans>

WzkTransactionManager(事务控制器)

/**
 * 事务的控制器 这里只是对 JDBC 等操作的简单封装
 * @author wzk
 * @date 11:31 2024/11/19
**/
public class WzkTransactionManager {

    private WzkConnectionUtils wzkConnectionUtils;

    public void setWzkConnectionUtils(WzkConnectionUtils wzkConnectionUtils) {
        this.wzkConnectionUtils = wzkConnectionUtils;
    }

    public void beginTransaction() throws SQLException {
        wzkConnectionUtils.getCurrentConnection().setAutoCommit(false);
    }

    public void commit() throws SQLException {
        wzkConnectionUtils.getCurrentConnection().commit();
    }

    public void rollback() throws SQLException {
        wzkConnectionUtils.getCurrentConnection().rollback();
    }

}

ProxyFactory(代理工厂)

/**
 * 代理工厂 + 单例模式
 * 参考代理设计模式 实现事务的控制
 * 这里主要实现的是动态代理的方式
 * @author wzk
 * @date 11:33 2024/11/19
**/
public class ProxyFactory {

    private WzkTransactionManager wzkTransactionManager;

    public void setWzkTransactionManager(WzkTransactionManager wzkTransactionManager) {
        this.wzkTransactionManager = wzkTransactionManager;
    }

    /**
     * 动态代理-JDK的实现
     * @author wzk
     * @date 11:35 2024/11/19
    **/
    public Object getJdkProxy(Object object) {
        return Proxy.newProxyInstance(
                object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object result;
                        // 开启事务
                        wzkTransactionManager.beginTransaction();
                        // 执行原来的方法
                        try {
                            result = method.invoke(object, args);
                        } catch (Exception e) {
                            // 回滚
                            wzkTransactionManager.rollback();
                            throw e;
                        }
                        // 提交事务
                        wzkTransactionManager.commit();
                        return result;
                    }
                }
        );
    }

    /**
     * 动态代理-CGLIB的实现
     * @author wzk
     * @date 13:57 2024/11/19
    **/
    public Object getCglibProxy(String object) {
        return Enhancer.create(
                object.getClass(),
                new MethodInterceptor() {
                    @Override
                    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                        Object result;
                        wzkTransactionManager.beginTransaction();
                        try {
                            result = method.invoke(object, args);
                        } catch (Exception e) {
                            wzkTransactionManager.rollback();
                            throw e;
                        }
                        wzkTransactionManager.commit();
                        return result;
                    }
                }
        );
    }
}

WzkServlet(控制器)

@WebServlet(name="wzkServlet", urlPatterns = "/wzkServlet")
public class WzkServlet extends HttpServlet {

    // 获取代理的对象 有事务 AOP 加持的
    private WzkTransferService wzkTransferService;

    @Override
    public void init() throws ServletException {
        super.init();
        // 从BeanFactory中国拿到代理工厂ProxyFactory 可以对某个类进行代理扩展
        // 这里扩展的功能点:事务提交与回滚
        ProxyFactory proxyFactory = (ProxyFactory) BeanFactory.getBean("proxyFactory");
        this.wzkTransferService = (WzkTransferService) proxyFactory.getJdkProxy(
                BeanFactory.getBean("wzkTransferService"));
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("=== WzkServlet doGet ===");
        // 执行业务逻辑
        System.out.println("wzkTransferService: " + wzkTransferService);
        try {
            wzkTransferService.transfer("1", "2", 100);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("=== transfer error ====");
        }
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().print("=== WzkServlet doGet ===");
    }

}

测试运行1(正常执行)

启动项目进行测试,这里测试顺利执行的情况。可以看到顺利的执行了,数据库的处理也是正常的。


测试运行2(异常回滚)

这里测试,当运行过程中出现问题,需要进行回滚的情况。我们需要在impl里WzkTransferServiceImpl的transfer代码中,随便加入一个异常,比如用1除以0这种:

@Override
public void transfer(String fromCard, String toCard, int money) throws Exception {
    WzkAccount from = wzkAccountDao.selectWzkAccount(fromCard);
    WzkAccount to = wzkAccountDao.selectWzkAccount(toCard);
    from.setMoney(from.getMoney() - money);
    to.setMoney(to.getMoney() + money);
    int fromResult = wzkAccountDao.updateWzkAccount(from);

    // 故意制造异常
    int i = 1 / 0;

    int toResult = wzkAccountDao.updateWzkAccount(to);
    System.out.println("transfer fromResult: " + fromResult + " toResult: " + toResult);
}

执行之后,数据库的数据应该不会变化(因为事务管理器回滚了)。


总结

本文通过手动实现AOP的方式,实现了事务的控制。这种简易的实现虽然不具备扩展能力,但能够帮助我们更好地理解AOP的原理以及事务管理的工作机制。后续可以基于此进行更复杂的AOP功能扩展。