前言:最近几个月很忙,都没有时间写文章了,今天周末刚好忙完下班相对早点(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

    好了, 以上就是全部的实现方式了,至于哪种更好,我认为在不同的场景下选择合适的实现方式即可,没有所谓的最好,存在即有意义,最后期待我下次再写新的博文吧!~

干货分享:小技巧大用处之Bean管理类工厂多种实现方式的更多相关文章

  1. Ioc容器-Bean管理(工厂bean)

    IoC操作Bean管理(FactoryBean) 1,Spring有两种类型bean,一种像自己创建的普通bean,另一种工厂bean(FactoryBean) 2,普通bean:在spring配置文 ...

  2. 【redis 学习系列08】Redis小功能大用处02 Pipeline、事务与Lua

    3.Pipeline 3.1 Pipeline概念 Redis客户端执行一条命令分为如下四个过程: (1)发送命令 (2)命令排队 (3)命令执行 (4)返回结果 其中(1)和(4)称为Round T ...

  3. Redis 发布订阅,小功能大用处,真没那么废材!

    今天小黑哥来跟大家介绍一下 Redis 发布/订阅功能. 也许有的小伙伴对这个功能比较陌生,不太清楚这个功能是干什么的,没关系小黑哥先来举个例子. 假设我们有这么一个业务场景,在网站下单支付以后,需要 ...

  4. JS小技巧大本事(持续更新)

    1. 复制N个字符 String.prototype.repeat = function(num){ return (new Array(++num)).join(this); } var a = ' ...

  5. 小扩展大用处,自己扩展一个ForeachRead吧

    是否用过IList的扩展方法 Foreach,而郁闷IEnumerable没有这个扩展?(没用过??用用吧,真的很方便,可以少好几行呢!!) 是否为了有一个索引而不得不用 for 而不能用 forea ...

  6. 【redis 学习系列07】Redis小功能大用处01 慢查询分析以及Redis Shell

    Redis提供了5种数据结构已经足够强大,但除此之外,Redis还提供了诸如慢查询分析.功能强大的Redis Shell.Pipeline.事务与Lua脚本.Bitmaps.HyperLogLog.发 ...

  7. redis小功能大用处-bitmaps

  8. 分享一个14年写的用户管理类-swift版

    AccountManager类 14年设计,从swift 0.9开始,迭代到现在swift4.0版本,总体几乎没什么改动,简单稳定. 其实现的思路主要还是借助之前net反射的经验,实现了自动保存用户信 ...

  9. Unity中容易被忽略的小技巧

    今天在游戏蛮牛上看到慕容小匹夫的一篇文章,感觉对自己现在的水平很实用,就给转载了过来,以便日后好温习一下. 这里还是要支持原创作者,原文地址在这里 一.编辑器染色 一个常见的工作情景是我们在调整场景内 ...

随机推荐

  1. netty系列之:netty中常用的xml编码解码器

    目录 简介 XmlFrameDecoder XmlDecoder 总结 简介 在json之前,xml是最常用的数据传输格式,虽然xml的冗余数据有点多,但是xml的结构简单清晰,至今仍然运用在程序中的 ...

  2. 【HarmonyOS学习笔记】Slider组件实现图形可调旋转

    哈喽大家好我是厚脸皮的小威 之前刚刚用华为的IDE跑通"HELLO,WORLD" 趁热又想去试试看跑一下基于TS拓展API接口的Slider组件,去实现图片的放大和缩小 凭借着大学 ...

  3. python之贪婪算法

    贪婪算法 贪婪算法也称为最优算法,这种算法并不是最准确的答案,但确认最接近答案的近似算法. 这时候有人会问,不是最准确的答案我要她干嘛?但是在日常中,我们有时候会遇到一些我们无法处理的问题,甚至是要花 ...

  4. Docker学习重点(7)~DockerFile

    一.DockerFile DockerFile是用来构建docker镜像的文件,可以理解为命令参数脚本! 1.构建步骤: 编写一个dockerfile文件 docker build 构建成为一个镜像 ...

  5. 用python实现自动化登录禅道系统 设置定时器自动执行脚本

    由于各种原因,我想试下用python实现自动登录禅道系统,并且每天定时执行.(本人第一次接触自动化,在大佬眼中门槛都没摸到的类型) 首先缕清思路: 1.实现自动登录禅道系统,用selenium实现2. ...

  6. 没错,就是Access-Control-Allow-Origin,跨域

    服务端添加: <add name="Access-Control-Allow-Origin" value="*" /><add name=&q ...

  7. FlinkSQL源码阅读-schema管理

    在Flink SQL中, 元数据的管理分为三层: catalog-> database-> table, 我们知道Flink SQL是依托calcite框架来进行SQL执行树生产,校验,优 ...

  8. 大功率超远距离lora无线数传电台,多级中继功能

    一.在无线通信领域,LoRa是目前市场最被看好的技术之一.随着新一代LoRa调制技术的升级,市场对LoRa技术的认知.认可逐步提高,基于LoRa调制技术开发的产品得到更广泛的应用.受益于其超低的接收灵 ...

  9. LVGL库入门教程04-样式

    LVGL样式 LVGL样式概述 创建样式 在 LVGL 中,样式都是以对象的方式存在,一个对象可以描述一种样式.每个控件都可以独立添加样式,创建的样式之间互不影响. 可以使用 lv_style_t 类 ...

  10. VirtualBox虚拟机安装Ubuntu系统后,增加内存空间和处理器核心数

    对于Linux爱好者而言,初次使用虚拟机时,一般都会使用默认的设置,例如硬盘空间.内存空间等等. 而往往在熟悉之后,安装了某些必要的软件,或者熟悉了实际的开发场景后,却发现原本给虚拟机分配的物理资源是 ...