乾貨分享:小技巧大用處之Bean管理類工廠多種實現方式

来源:https://www.cnblogs.com/zuowj/archive/2022/07/30/16534102.html
-Advertisement-
Play Games

前言:最近幾個月很忙,都沒有時間寫文章了,今天周末剛好忙完下班相對早點(20:00下班)就在家把之前想總結的知識點寫出來,於是就有了這篇文章。雖無很高深的技術,但小技巧有大用處。 有時我們經常需要將實現了某個基類或某個介面的所有Bean進行分類管理,在需要用到的時候按需獲取實現了某個基類或某個介面的 ...


前言:最近幾個月很忙,都沒有時間寫文章了,今天周末剛好忙完下班相對早點(20:00下班)就在家把之前想總結的知識點寫出來,於是就有了這篇文章。雖無很高深的技術,但小技巧有大用處。

有時我們經常需要將實現了某個基類或某個介面的所有Bean進行分類管理,在需要用到的時候按需獲取實現了某個基類或某個介面的Bean實例對象,那麼我們就需要Bean管理類工廠(即:工廠模式),實現Bean管理類工廠我總結了目前已知且常用的實現方式,敬請各位看官欣賞,如是不足或更好建議歡迎評論區留言指正,謝謝!

為了便於演示,我先自定義如下介面:

/**
 * @author zuowenjun
 *  <pre>www.zuowenjun.cn</pre>
 */
public interface IDemo {
    String getValue();
    int doFor();
}

然後定義3個實現了上述介面的Service Bean類:(註意到Bean類上方還有@DemoFactoryNeedBean這個先不用管,後面的方式中會有用到)

@Service
public class DemoService1 implements IDemo {

    @Override
    public String getValue() {
        return "DemoService1.getValue by 夢在旅途 zuowj.cnblogs.com";
    }

    @Override
    public int doFor() {
        return 1;
    }
}

@DemoFactoryNeedBean
@Service
public class DemoService2 implements IDemo {

    @Override
    public String getValue() {
        return "DemoService2.getValue by 夢在旅途 zuowj.cnblogs.com";
    }

    @Override
    public int doFor() {
        return 2;
    }
}

@DemoFactoryNeedBean
@Service
public class DemoService3 implements IDemo {

    @Override
    public String getValue() {
        return "DemoService3.getValue by 夢在旅途 zuowj.cnblogs.com";
    }

    @Override
    public int doFor() {
        return 3;
    }
}

下麵直接無廢話列舉各種實現方式

  1. 實現方式一:直接使用集合的依賴註入方式(利用spring註入時會判斷是否為集合,若為集合則獲取所有實現了該類的BEAN集合併進行註入)

    /**
     * @author zuowenjun
     *  <pre>www.zuowenjun.cn</pre>
     */
    @Service
    public class DemoFactory1 {
    
        @Autowired
        private List<IDemo> demos;
    
        public IDemo getOne(int index){
            return demos.stream().filter(d->d.doFor()==index).findFirst().orElseThrow(()->new IllegalArgumentException("not found demo bean"));
        }
    }
    

    單元測試【DemoFactory1】BEAN管理工廠用法及結果:

        @Autowired
        private DemoFactory1 demoFactory1;
        
            @Test
        public void testDemoFactory1(){
            for (int i=1;i<=3;i++){
                    IDemo demo = demoFactory1.getOne(i);
                    System.out.printf("testDemoFactory1--bean class: %s , getValue:%s,  doFor:%d %n", demo.getClass().getSimpleName(), demo.getValue(), demo.doFor());
            }
        }
    

    運行結果:

    testDemoFactory1--bean class: DemoService1 , getValue:DemoService1.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:1
    testDemoFactory1--bean class: DemoService2 , getValue:DemoService2.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:2
    testDemoFactory1--bean class: DemoService3 , getValue:DemoService3.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:3

  2. 實現方式二:通過實現BeanPostProcessor介面,利用每個BEAN實例化後均會調用postProcessAfterInitialization方法的特點,直接在postProcessAfterInitialization方法中收集所需的BEAN實例並添加到集合中

    /**
     * @author zuowenjun
     *  <pre>www.zuowenjun.cn</pre>
     */
    @Service
    public class DemoFactory2 implements BeanPostProcessor {
    
        private List<IDemo> demos=new ArrayList<>();
    
        public IDemo getOne(int index){
            return demos.stream().filter(d->d.doFor()==index).findFirst().orElseThrow(()->new IllegalArgumentException("not found demo bean"));
        }
    
        @Override
        @Nullable
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof IDemo) {
                System.out.printf("postProcessAfterInitialization->bean class:%s",bean.getClass().getSimpleName());
                demos.add((IDemo) bean);
            }
            return bean;
        }
    }
    

    單元測試【DemoFactory2】BEAN管理工廠用法及結果:

        @Autowired
        private DemoFactory2 demoFactory2;
        
            @Test
        public void testDemoFactory2(){
            for (int i=1;i<=3;i++){
                IDemo demo= demoFactory2.getOne(i);
                System.out.printf("testDemoFactory2--bean class: %s , getValue:%s,  doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
            }
        }
    

    運行結果:

    testDemoFactory2--bean class: DemoService1 , getValue:DemoService1.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:1
    testDemoFactory2--bean class: DemoService2 , getValue:DemoService2.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:2
    testDemoFactory2--bean class: DemoService3 , getValue:DemoService3.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:3

  3. 實現方式三:通過實現ApplicationRunner、ApplicationContextAware介面,以便在setApplicationContext能獲取到上下文實例對象並保存,然後在spring初始化完成執行run方法中使用上下文實例對象獲取指定類型的BEAN實例集合。當然也可以不用實現ApplicationRunner介面,而是在工廠方法獲取BEAN對象第一次時才用上下文實例對象獲取指定類型的BEAN實例集合(即:初始化一次)如代碼中的getOneForLazy方法所示。

    /**
     * @author zuowenjun
     *  <pre>www.zuowenjun.cn</pre>
     */
    @Service
    public class DemoFactory3 implements ApplicationRunner, ApplicationContextAware {
    
        private ApplicationContext context;
    
        @Autowired
        private List<IDemo> demos;
    
        public IDemo getOne(int index) {
            return demos.stream().filter(d -> d.doFor() == index).findFirst().orElseThrow(() -> new IllegalArgumentException("not found demo bean"));
        }
    
        public IDemo getOneForLazy(int index) {
            if (CollectionUtils.isEmpty(demos)){
                demos = new ArrayList<>(context.getBeansOfType(IDemo.class).values());
            }
            return demos.stream().filter(d -> d.doFor() == index).findFirst().orElseThrow(() -> new IllegalArgumentException("not found demo bean"));
        }
    
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            demos = new ArrayList<>(context.getBeansOfType(IDemo.class).values());
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.context = applicationContext;
        }
    }
    

    單元測試【DemoFactory3】BEAN管理工廠用法及結果:

        @Autowired
        private DemoFactory3 demoFactory3;
    
        @Test
        public void testDemoFactory3(){
            for (int i=1;i<=3;i++){
                IDemo demo= demoFactory3.getOne(i);
                System.out.printf("testDemoFactory3--bean class: %s , getValue:%s,  doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
            }
        }
    

    運行結果:

    testDemoFactory3--bean class: DemoService1 , getValue:DemoService1.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:1
    testDemoFactory3--bean class: DemoService2 , getValue:DemoService2.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:2
    testDemoFactory3--bean class: DemoService3 , getValue:DemoService3.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:3

  4. 實現方式四:此為組合模式,先定義註入ApplicationContext上下文對象,然後定義一個枚舉類,在枚舉類中為每個枚舉項都指明BEAN的實現類型,最後需要獲取BEAN實例時,直接根據上下文對象獲取指定類型的BEAN實例即可。

    
    /**
     * @author zuowenjun
     * <pre>www.zuowenjun.cn</pre>
     */
    @Service
    public class DemoFactory4 {
    
        private final ApplicationContext context;
    
        public DemoFactory4(ApplicationContext context) {
            this.context = context;
        }
    
        public IDemo getOne(DemoFactory4Enum factory4Enum) {
            return context.getBean(factory4Enum.getBeanClass());
        }
    
    
        public enum DemoFactory4Enum {
            Demo1(1, DemoService1.class),
            Demo2(2, DemoService2.class),
            Demo3(3, DemoService3.class),
            ;
    
            private final Class<? extends IDemo> beanClass;
            private final int index;
    
            DemoFactory4Enum(int i, Class<? extends IDemo> beanClass) {
                this.index = i;
                this.beanClass = beanClass;
            }
    
            public Class<? extends IDemo> getBeanClass() {
                return beanClass;
            }
    
            public int getIndex() {
                return index;
            }
    
            public static DemoFactory4Enum parse(int i){
                return Arrays.stream(values()).filter(d->d.getIndex()==i).findFirst().orElseThrow(()->new IllegalArgumentException("not found enum item!"));
            }
    
        }
    }
    

    單元測試【DemoFactory4】BEAN管理工廠用法及結果:(演示了2種方式,當然本質都是先確定枚舉項,再獲取BEAN對象)

        @Autowired
        private DemoFactory4 demoFactory4;
        
            @Test
        public void testDemoFactory4(){
    //        for (DemoFactory4.DemoFactory4Enum enumItem:DemoFactory4.DemoFactory4Enum.values()){
    //            IDemo demo= demoFactory4.getOne(enumItem);
    //            System.out.printf("testDemoFactory4--bean class: %s , getValue:%s,  doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
    //        }
    
            for (int i=1;i<=3;i++){
                IDemo demo= demoFactory4.getOne(DemoFactory4.DemoFactory4Enum.parse(i));
                System.out.printf("testDemoFactory4--bean class: %s , getValue:%s,  doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
            }
        }
    

    運行結果:

    testDemoFactory4--bean class: DemoService1 , getValue:DemoService1.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:1
    testDemoFactory4--bean class: DemoService2 , getValue:DemoService2.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:2
    testDemoFactory4--bean class: DemoService3 , getValue:DemoService3.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:3

  5. 實現方式五:此為組合模式,與實現方式四有點類似,但又有不同,仍然是先定義註入ApplicationContext上下文對象,然後定義一個抽象枚舉類(有一個抽象方法,如:getBean),在枚舉類中為每個枚舉項都實現這個抽象方法,在抽象方法中通過靜態上下文對象欄位來獲取指定類型的BEAN實例,最後需要獲取BEAN實例就比較簡單了,只要得到枚舉項,就可以直接獲取到對應的BEAN實例。

    
    /**
     * @author zuowenjun
     *  <pre>www.zuowenjun.cn</pre>
     */
    @Service
    public class DemoFactory5 {
    
        private static ApplicationContext context;
    
        public DemoFactory5(ApplicationContext context) {
            DemoFactory5.context = context;
        }
    
        public enum DemosEnum {
            Demo1(1) {
                @Override
                public IDemo getBean() {
                    return context.getBean(DemoService1.class);
                }
            },
            Demo2(2) {
                @Override
                public IDemo getBean() {
                    return context.getBean(DemoService2.class);
                }
            },
            Demo3(3) {
                @Override
                public IDemo getBean() {
                    return context.getBean(DemoService3.class);
                }
            },
            ;
    
            private final int index;
    
            DemosEnum(int index) {
                this.index = index;
            }
    
            public int getIndex() {
                return index;
            }
    
            public abstract IDemo getBean();
    
    
            public static DemosEnum parse(int i){
                return Arrays.stream(values()).filter(d->d.getIndex()==i).findFirst().orElseThrow(()->new IllegalArgumentException("not found enum item!"));
            }
    
        }
    }
    

    單元測試【DemoFactory5】BEAN管理工廠用法及結果:(演示了2種方式,當然本質都是先確定枚舉項,再獲取BEAN對象)

        @Test
        public void testDemoFactory5(){
    //        for (DemoFactory5.DemosEnum demosEnum:DemoFactory5.DemosEnum.values()){
    //            IDemo demo= demosEnum.getBean();
    //            System.out.printf("testDemoFactory5--bean class: %s , getValue:%s,  doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
    //        }
    
            for (int i=1;i<=3;i++){
                IDemo demo= DemoFactory5.DemosEnum.parse(i).getBean();
                System.out.printf("testDemoFactory5--bean class: %s , getValue:%s,  doFor:%d %n",demo.getClass().getSimpleName(),demo.getValue(),demo.doFor());
            }
        }
    

    運行結果:

    testDemoFactory5--bean class: DemoService1 , getValue:DemoService1.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:1
    testDemoFactory5--bean class: DemoService2 , getValue:DemoService2.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:2
    testDemoFactory5--bean class: DemoService3 , getValue:DemoService3.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:3

  6. 實現方式六:其實本質還是實現方式一的靈活應用,通過自定義標註了@Qualifier註解的過濾註解類(如:@DemoFactoryNeedBean),然後在對應的BEAN類上加上該自定義的過濾註解,最後在工廠類的內部集合依賴註入欄位上同樣增加自定義的過濾註解,這樣就可以在原有的基礎上(BEAN的基類或介面)增加過濾必需包含指明瞭自定義過濾註解的BEAN實例集合。

    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Qualifier
    public @interface DemoFactoryNeedBean {
    }
    
    /**
     * @author zuowenjun
     *  <pre>www.zuowenjun.cn</pre>
     */
    @Service
    public class DemoFactory1 {
    
        @DemoFactoryNeedBean
        @Autowired
        private List<IDemo> demos;
    
        public IDemo getOne(int index){
            return demos.stream().filter(d->d.doFor()==index).findFirst().orElseThrow(()->new IllegalArgumentException("not found demo bean"));
        }
    
        public boolean hasBean(int index){
            return demos.stream().anyMatch(d->d.doFor()==index);
        }
    }
    

    然後再看文章開頭定義的3個BEAN類,其中:DemoService2、DemoService3是有加@DemoFactoryNeedBean註解的,最後再次單元測試【DemoFactory1】BEAN管理工廠用法及結果:

        @Test
        public void testDemoFactory1(){
            for (int i=1;i<=3;i++){
                if (demoFactory1.hasBean(i)) {
                    IDemo demo = demoFactory1.getOne(i);
                    System.out.printf("testDemoFactory1--bean class: %s , getValue:%s,  doFor:%d %n", demo.getClass().getSimpleName(), demo.getValue(), demo.doFor());
                }
            }
        }
    

    運行結果:(少了DemoService1 的BEAN)

    testDemoFactory1--bean class: DemoService2 , getValue:DemoService2.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:2
    testDemoFactory1--bean class: DemoService3 , getValue:DemoService3.getValue by 夢在旅途 zuowj.cnblogs.com, doFor:3

    好了, 以上就是全部的實現方式了,至於哪種更好,我認為在不同的場景下選擇合適的實現方式即可,沒有所謂的最好,存在即有意義,最後期待我下次再寫新的博文吧!~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 為什麼要參加軟考: 軟考全稱是電腦技術與軟體專業技術資格考試,學生可以為畢業後面試錦上添花,已參加工作且不是本專業的拿個證在一定程度上彌補不是本專業的劣勢。如果你要往架構師、項目經理等晉升,有些企業面試會有證優先。另外評職稱、積分落戶等都一定用處。本人屬於已參加工作。 —————————————— ...
  • 1、引例 【例1】 分析該程式,有哪些問題 int main() { swap(int p, int q); int a = 10, b = 20; printf("(1)a = %d, b = %d\n", a, b); swap(&a, &b); printf("(2)a = %d, b = % ...
  • 精華筆記: 1. 運算符: - 算術:+、-、*、/、%、++、-- - 關係:>、<、>=、<=、==、!= - 邏輯:&&、||、! - 賦值:=、+=、-=、*=、/=、%= - 字元串連接:+ - 條件/三目:boolean?數1:數2 2. 分支結構:基於條件執行的語句 - if結構:1條 ...
  • 精華筆記: 1. 變數:存數的 - 聲明: 在銀行開了個帳戶 - 初始化: 給帳戶存錢 - 使用: 使用的是帳戶裡面的錢 - 對變數的使用就是對它所存的那個數的使用 - 變數在使用之前必須聲明並初始化 - 命名: - 只能包含字母、數字、_和$符,並且不能以數字開頭 - 嚴格區分大小寫 - 不能使用 ...
  • 一、python簡介:是什麼,特點是什麼,有什麼用 1、python是什麼? python是一門結合解釋型,編譯性,互動性和麵向對象的腳本語言,具有很強的可讀性,相比其他例如Java語言,C語言更加容易入門。 2、python有哪些特點? 易於學習:python有相對較少的關鍵字,結構簡單,和一個明 ...
  • JetBrAIns GoLand 是Mac os系統上由JetBrAIns推出的一個GO語言集成開發工具環境,基於IntelliJ平臺,支持JetBrAIns插件體系,擁有針對GO語言的代碼助手、代碼編輯器、代碼調試等工具,支持前端和後端開發,並且支持IntelliJ插件,可以大大提高Go語言開發者 ...
  • 配置不同生產環境 本文適用於開發環境下需要打包項目至生產環境,避免開發環境的配置文件泄露。 設置maven 作用:1. 手動調節運行時的不同環境 2. 打包時可以不會有其它環境的文件 註:每次換環境前(打包前)記得手動clean清楚,因為idea不會在換環境後自動清除另一個環境的文件 在pom文件中 ...
  • 介紹 本篇文章主要針對於電腦二級考試的崽崽,當然想瞭解Python和學習Python的崽崽也是可以看本篇文章的;畢竟,手機和電腦都可以運行Python;本篇我文章雖然是筆記,但是也純靠手打,希望關註和點贊一下,期待我的其他隨筆和文章;文章作者由博客園狐小妖用戶撰寫,非來自於博客園且不帶轉載註明,均 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...