全棧之路-雜篇-探究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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM