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

    现在我们想要展示一些可视化内容 首先导包,如果是在jupyter notebook上,需要加入魔法函数:%matplotlib inline,这表示可以在jupyter上直接画图 import dat ...

  2. nslookup:command not found的解决办法

    nslookup:command not found的解决办法 通过nslookup查看DNS记录,在这里遇到了一个小插曲,nslookup:command not found(未找到命令),是因为新 ...

  3. JavaScript 单线程之异步编程

    Js 单线程之异步编程 先了解一个概念,为什么 JavaScript 采用单线程模式工作,最初设计这门语言的初衷是为了让它运行在浏览器上面.它的目的是为了实现页面的动态交互,而交互的核心是进行 Dom ...

  4. ArrayList常用Api分析及注意事项

    数组(定长,有序的,随机访问).ArrayList是Java在数组的基础上进行衍生出来的Java里的一种数据结构,它在拥有数据的特性之外,增加了可变性 (动态数组). 属性 属性 备注 DEFAULT ...

  5. MongoDB 分片集群

    每日一句 Medalist don't grow on trees, you have to nurture them with love, with hard work, with dedicati ...

  6. 聊聊OOP中的设计原则以及访问者模式

    一  设计原则 (SOLID) 1.  S - 单一职责原则(Single Responsibllity Principle) 1.1  定义 一个类或者模块只负责完成一个职责(或功能), 认为&qu ...

  7. @vue/cli3+配置build命令构建测试包&正式包

    上一篇博客介绍了vue-cli2.x配置build命令构建测试包和正式包,但现在前端开发vue项目大多数使用新版@vue/cli脚手架搭建vue项目(vue create project-name) ...

  8. Linux系统安装ActiveMQ

    下载安装包 https://activemq.apache.org/components/classic/download/ 上传至服务器并解压 [root@localhost activemq]# ...

  9. [WUSTCTF2020]颜值成绩查询-1

    分享下自己在完成[WUSTCTF2020]颜值成绩查询-1关卡的手工过程和自动化脚本. 1.通过payload:1,payload:1 ,payload:1 or 1=1--+,进行判断是否存在注入, ...

  10. flink窗口分类

    窗口分类 按照驱动类型分类 窗口本身是截取有界数据的一种方式,所以窗口一个非常重要的信息就是"怎样截取数据".换句话说,就是以什么标准来开发和结束数据的截取. 按照驱动类型分类主要 ...