浅入浅出代理模式与 Spring 事务管理

本文深入探讨了Spring的代理模式与事务管理。通过实例解释了代理模式的静态和动态代理,强调了动态代理在减少重复代码和提高灵活性方面的作用。接着详细介绍了Spring事务管理的注解使用和原理,包括PlatformTransactionManager接口的功能,以及TransactionInterceptor在事务处理中的作用。当在同一个类内非事务方法调用事务方法时,由于没有生成代理对象,事务不会生效。
摘要由CSDN通过智能技术生成

前言

最近在开发业务代码的时候,犯了一个事务注解的错误:在同一个类的非事务方法中调用了另一个事务方法,导致事务没有生效,如下所示:

public ConfirmOrderResultVO batchConfirmPurchaseOrders(Long taobaoUserId, List<String> bizOrderIds) throws TCException {
        ………………………………………………………………
        for (String bizOrderId : bizOrderIds) {
            // 推单成功进入successList,否则进入failedList
            if (confirmPurchaseOrder(taobaoUserId, bizOrderId)){
                successList.add(Long.valueOf(bizOrderId));
            }else {
                failedList.add(Long.valueOf(bizOrderId));
            }
        }
        ………………………………………………………………
    }

其中confirmPurchaseOrder()是一个事务方法:

@Transactional
public Boolean confirmPurchaseOrder(Long taobaoUserId, String bizOrderId) throws TCException {
      logger.warn("|ConfirmPurchaseOrder|UserId:"+taobaoUserId+",orderId:"+bizOrderId);
      …………………………
      return ture;
}

这样在直接调用batchConfirmPurchaseOrders()方法时,如果confirmPurchaseOrder()方法发生了异常,是不会回滚的。原理在于Spring的事务管理依靠的动态代理模式,当在同一个类中调用一个非事务方法,是不会生成代理对象的,自然也不会触发事务。借此机会回顾一下代理模式和Spring事务管理的原理。

代理模式

网上讲解代理模式的文章千奇百怪,很多时候看完了也不明白讲的重点是什么。

事实上在生活中我们常常会遇到各种各样的代理模式,例如火车票代售点代理出售火车票,他在“帮忙”出售火车票的同时,收取了额外的手续费,记录了自己店里的流水等。

又比如班长代理老师来上交班费,他在“上交班费”的动作之前,还进行了检查班级同学是否到齐、向每一位同学收班费、核对班费金额,最后再上交班费。 

总而言之,代理模式就是代理其他对象,在完成原动作的基础上(前后)做一些额外的自定义的工作。

聪明的朋友看到这里一定想到了:那我在一个方法中调用另一个方法不就是代理模式了吗?啊对对对,从功能上来讲是一样的,但是“模式”之所以为“模式”,以我拙见,其本质目的在于形成规范,以减少重复代码的编写。因此如果不能达到减少代码重复这一本质目的,便不能称之为“模式”。

按照代理模式的实现方式,分为两种:静态代理和动态代理。那我们就拿刚刚说过的“写作业”这件事来做例子,讲解一下两种实现方式的区别。

静态代理

首先定义一个“作业”接口,里面有一个方法“做作业”

public interface Homework {
    void doHomework();
}

小红实现了这个接口。但是小红是一个不爱学习的小学生,又因为师从马掌门成为了学校的扛把子,所以为了不让老师发现,他决定自己随便做做,把剩下的部分交给小弟们来做。

public class XiaoHong implements Homework{
    @Override
    public void doHomework() {
        System.out.println("XiaoHong did homework casually");
    }
}

其中小王、小张两个小弟成绩优异,一个负责数学作业,一个负责英语作业,于是他们两个自告奋勇实现了Homework接口,在代理完成作业的基础上,还不断学习提高自己的能力,并且对作业答案进行了校验。

小王:

public class XiaoWang implements Homework{

    // 持有Homework属性
    private Homework homework;
    // 通过构造函数初始化Homework
    public XiaoWang(Homework homework){
        this.homework=homework;
    }

    //代理实现
    @Override
    public void doHomework() {
        doStudy();
        homework.doHomework();
        System.out.println("XiaoWang helps with MathHomework");
        doCheck();
    }

    // 额外方法 
    private void doCheck() {
        System.out.println("XiaoWang is checking-----------------");
    }
    // 额外方法 
    private void doStudy() {
        System.out.println("XiaoWang is studying---------------");
    }
}

小张:

public class XiaoZhang implements Homework{

    // 持有Homework属性
    private Homework homework;
    // 通过构造函数初始化Homework
    public XiaoZhang(Homework homework){
        this.homework=homework;
    }

    //代理实现
    @Override
    public void doHomework() {
        doStudy();
        homework.doHomework();
        System.out.println("XiaoZhang helps with EngHomework");
        doCheck();
    }

    // 额外方法
    private void doCheck() {
        System.out.println("XiaoZhang is checking-----------------");
    }
    // 额外方法
    private void doStudy() {
        System.out.println("XiaoZhang is studying---------------");
    }
}

于是,小红可以放心的把作业交给小王和小张了:

public static void main(String[] args) {
        // 实例化一个目标对象
        XiaoHong xiaoHong = new XiaoHong();
        // 把目标对象通过构造函数传递给代理对象
        XiaoZhang xiaoZhang =new XiaoZhang(xiaoHong);
        XiaoWang xiaoWang =new XiaoWang(xiaoHong);
        // 调用代理对象的方法
        xiaoZhang.doHomework();
        xiaoWang.doHomework();
    }

输出:

XiaoZhang is studying---------------
XiaoHong did homework casually
XiaoZhang helps with EngHomework
XiaoZhang is checking-----------------

XiaoWang is studying---------------
XiaoHong did homework casually
XiaoWang helps with MathHomework
XiaoWang is checking-----------------

问题来了,如果老师又布置了一个Book接口和readBook方法,但是readBook前后的动作是一样的(都需要study和check),如何通过代理来实现呢?

一个方案是让小王和小张都实现Book接口和readBook方法,并持有新的Book对象;另一个方案是再找一个小赵实现Book接口和readBook方法。

这样两种方案实际上都能达到效果,但是如果接口和方法增多起来呢?如果让一个代理类实现一堆方法,并持有一堆不同的对象,这个类势必会变得臃肿不堪;如果每一个新的方法都创建一个新的代理类,引用不同的对象,又会造成代码的大量重复。因此,动态代理就出现了。

动态代理

前文所讲的静态代理之所以为“静态”,是因为每个接口的每个方法,我们都要显式的去实现、创建、调用,所有的操作都是写死的、是静

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倾听铃的声

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值