zoukankan      html  css  js  c++  java
  • 全栈之路-杂篇-探究SpringBoot多例模式注入的方案

      毫无疑问的,springboot托管的实体类默认是以单例模式的形式进行实例化的,但是在某些场景下,我们需要的不是单例模式的实体类,这个时候我们该如何去实现springboot托管的实体类进行多例模式的创建呢?

    一、单例模式存在的问题

    1、业务场景介绍

      就是我们为什么会有改变springboot默认单例模式的这种创建bean的形式?这个业务场景是这样的,我们在做优惠券的校验的时候,我们自己封装了一个优惠券的校验类,这样的话,我们就可以很方便的进行统一的优惠券的校验了,在做优惠券的校验的时候,我们会有一个打折的情况,这里会出现一个四舍五入的问题,就会导致数据并不是原来的数据,会有一个偏差,我们到底是采用什么模式进行小数的取舍,这里是值得探讨的,但是我们这里的处理方式是将折扣价的计算封装成了一个接口,这样我们写了几个实现类,有四舍五入的方式,有直接舍去的方式,有银行家算法的方式,我们在使用的时候直接使用实现类来计算就好,在代码中我们是这样实现的:

    接口类:

    1 // 接口类
    2 public interface IMoneyDiscount {
    3     BigDecimal discount(BigDecimal original, BigDecimal discount);
    4 }

    第一个实现类(四舍五入):

    1 public class HalfUpRound implements IMoneyDiscount{
    2 
    3     @Override
    4     public BigDecimal discount(BigDecimal original, BigDecimal discount) {
    5         BigDecimal actualMoney = original.multiply(discount);
    6         BigDecimal finalMoney = actualMoney.setScale(2, RoundingMode.HALF_UP);
    7         return finalMoney;
    8     }
    9 }

    第二个实现类(向上取整):

    1 public class UpRound implements IMoneyDiscount{
    2     @Override
    3     public BigDecimal discount(BigDecimal original, BigDecimal discount) {
    4         BigDecimal actualMoney = original.multiply(discount);
    5         BigDecimal finalMoney = actualMoney.setScale(2, RoundingMode.UP);
    6         return finalMoney;
    7     }
    8 }

    第三个实现类(银行家算法):

    1 public class HalfEvenRound implements IMoneyDiscount{
    2     @Override
    3     public BigDecimal discount(BigDecimal original, BigDecimal discount) {
    4         BigDecimal actualMoney = original.multiply(discount);
    5         BigDecimal finalMoney = actualMoney.setScale(2, RoundingMode.HALF_EVEN);
    6         return finalMoney;
    7     }
    8 }

    我们在做订单校验的时候,需要对优惠券进行校验,在做优惠券校验的时候,我们构建了一个CouponChecker的优惠券校验类,在这个类中我们会做订单金额的校验,当然可以直接使用new对象的方法,来使用折扣计算的方法,但是我们这里使用的是注入的方式来优化这个,但是这样就存在问题了,我们需要的是每一个订单都是有每一个校验的,这时候单例模式就存在问题了,因为我们是需要Coupon类和UserCoupon类同时作为参数传入进行CouponChecker类中的,也就是我们通过传统的new对象的方式进行校验,但是如果是单例模式的话,这些是没有办法使用的。

    2、单例模式下的实体类不能有自己私有的成员变量的,即使有的话,那么这些成员变量也必须是单例模式的,所以我们必须解决这个单例模式的问题,来符合我们业务的需要

    二、解决方案

    1、我们把CouponChecker作为一个不在springboot容器中托管的类,只是作为一个普通类来操作,这样我们在service层,也就是OrderService类中将IMoneyDiscount的实现类作为参数传入到CouponChecker中,这样可以解决那个单例模式存在的问题,代码如下:

    (1)OrderService类,来处理order订单相关的逻辑类:

     1 @Service
     2 public class OrderService {
     3     // 省略部分代码......
     4 
     5     @Autowired
     6     private IMoneyDiscount iMoneyDiscount;
     7 
     8     /**
     9      * 订单校验的主方法
    10      *
    11      * @param uid      用户id
    12      * @param orderDTO 订单相关数据
    13      */
    14     public void isOk(Long uid, OrderDTO orderDTO) {
    15         // 省略部分代码......
    16         // 数据的校验
    17         CouponChecker couponChecker = new CouponChecker(coupon, userCoupon, this.iMoneyDiscount);
    18 
    19     }

    (2)CouponChecker类,来进行优惠券的校验

     1 // @Service 不需要在这里让springboot容器托管
     2 public class CouponChecker {
     3 
     4 //    @Autowired
     5     private IMoneyDiscount iMoneyDiscount;
     6 
     7     private Coupon coupon;
     8     private UserCoupon userCoupon;
     9 // 这里在构造方法中进行传入进来
    10     public CouponChecker(Coupon coupon, UserCoupon userCoupon, IMoneyDiscount iMoneyDiscount) {
    11         this.coupon = coupon;
    12         this.userCoupon = userCoupon;
    13         this.iMoneyDiscount = iMoneyDiscount;
    14     }
    15     // 省略部分校验方法逻辑代码......
    16 }

    这种方法是可以解决我们上面提到的那个单例模式存在的问题的,但是这种方法可能不是很完美,我们还有更好的解决方法

    2、使用@Scope注解

    这里具体的应用方案好像在这里并不是很实用,具体使用方法我想了一下,不知道怎么使用!!!讲一下@Scope注解是怎么使用的吧

    (1)实体类代码

    1 @Getter
    2 @Setter
    3 @Component
    4 @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
    5 public class Test {
    6     private String name;
    7 }

    (2)controller层代码

     1 @RestController
     2 @RequestMapping(value = "/test")
     3 public class TestController {
     4 
     5     @Autowired
     6     private Test test;
     7 
     8     @GetMapping(value = "")
     9     public void getDetail() {
    10         System.out.println(this.test);
    11     }
    12 }

    注意:这里只是@Scope注解的一个使用,具体怎么在那个业务场景下使用,我这里还暂时没有想到很好的办法!!!

     内容出处:七月老师《从Java后端到全栈》视频课程

    七月老师课程链接:https://class.imooc.com/sale/javafullstack

  • 相关阅读:
    vue分享二维码
    Linux的软件安装
    linux命令操作
    禅道的使用方法
    测试管理工具的安装和介绍
    测试Bug
    软件缺陷和软件缺陷的种类
    测试计划和测试用例
    双肩包,椅子,电梯的测试用例
    软件生命周期的模型
  • 原文地址:https://www.cnblogs.com/ssh-html/p/14050835.html
Copyright © 2011-2022 走看看