Spring 中的 BeanFactory 与 FactoryBean

1.前提概要

不少java开发者在使用Spring框架中都见事后缀为FactoryBean的类,好比Mybatis-Spring中的SqlSessionFactoryBean。说到这里就不得不提BeanFactory。FactoryBean和BeanFactory特别容易让人混淆,面试还常常问到这两种概念。其实它们的做用和使用场景是不同的java

2.BeanFactory

先来讲说BeanFactory。 用于访问Spring bean容器的根接口。这是Spring bean容器的基本客户端视图。原来是获取Spring Bean的接口,也就是IoC容器。而后咱们看类图面试

原来咱们更经常使用的ApplicationContext就是一个BeanFactory。咱们经过bean的名称或者类型均可以从BeanFactory来获取bean。对于BeanFactory这么介绍相信都不陌生了。让咱们把关注点转向FactoryBean上。segmentfault

3.FactoryBean

FactoryBean 是个什么玩意儿呢?来看看源码框架

public interface FactoryBean<T> {

              @Nullable
             T getObject() throws Exception;

 
              @Nullable
             Class<?> getObjectType();

 
             default boolean isSingleton() {
               return true;
             }
         }
  • T getObject() 获取泛型T的实例。用来建立Bean。当IoC容器经过getBean方法来FactoryBean建立的实例时实际获取的不是FactoryBean 自己而是具体建立的T泛型实例。等下咱们会来验证这个事情。
  • Class<?> getObjectType() 获取 T getObject()中的返回值 T 的具体类型。这里强烈建议若是T是一个接口,返回其具体实现类的类型。
  • default boolean isSingleton() 用来规定 Factory建立的的bean是不是单例。这里经过默认方法定义为单例。

3.1 FactoryBean使用场景

FactoryBean 用来建立一类bean。好比你有一些同属鸟类的bean须要被建立,可是它们本身有各自的特色,你只须要把他们的特色注入FactoryBean中就能够生产出各类鸟类的实例。举一个更加贴近实际生产的例子。甚至这个例子你能够应用到实际java开发中去。咱们须要本身造一个定时任务的轮子。用FactoryBean 再合适不过了。咱们来用代码说话一步步来演示FactoryBean的使用场景。ide

3.2 构建一个FactoryBean

咱们声明定时任务通常具备下列要素:测试

  • 时间周期,确定会使用到cron表达式。
  • 一个任务的执行抽象接口。
  • 定时任务具体行为的执行者。

Task任务执行抽象接口的实现。实现包含两个方面:this

  • SomeService 是具体任务的执行逻辑。
  • cron时间表达式
public class CustomTask implements Task {
    private SomeService someService;
    private String cronExpression;

    public CustomTask(SomeService someService) {
        this.someService = someService;
    }

    @Override
    public void execute() {
        //do something
        someService.doTask();
    }

    @Override
    public void setCron(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    @Override
    public String getCron() {
        return cronExpression;
    }
}

经过以上的定义。任务的时间和任务的逻辑能够根据不一样的业务作到差别化配置。而后咱们实现一个关于Task的FactoryBean。spa

public class TaskFactoryBean implements FactoryBean<Task> {
    private SomeService someService;
    private String cronExpression;


    @Override
    public Task getObject() throws Exception {
        CustomTask customTask = new CustomTask(someService);
        customTask.setCron(cronExpression);
        return customTask;
    }

    @Override
    public Class<?> getObjectType() {
        return CustomTask.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    public SomeService getSomeService() {
        return someService;
    }

    public void setSomeService(SomeService someService) {
        this.someService = someService;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }
}

3.3 FactoryBean 注入IoC

你可使用xml的注入方式,固然也可使用javaConfig的配置方式。这里咱们使用javaConfig注入。咱们将两个FactroyBean注入到Spring容器中去。debug

@Configuration
public class Config {

    @Bean
    public TaskFactoryBean customTask() {
        TaskFactoryBean taskFactoryBean = new TaskFactoryBean();
        taskFactoryBean.setCronExpression("0 15 10 * * ?");
        String word = "定时任务一";
        SomeService someService = new SomeService();
        someService.setWord(word);
        taskFactoryBean.setSomeService(someService);
        return taskFactoryBean;
    }

    @Bean
    public TaskFactoryBean otherTask() {
        TaskFactoryBean taskFactoryBean = new TaskFactoryBean();
        taskFactoryBean.setCronExpression("0 15 17 * * ?");
        String word = "定时任务二";
        SomeService someService = new SomeService();
        someService.setWord(word);
        taskFactoryBean.setSomeService(someService);
        return taskFactoryBean;
    }
}

3.4 FactoryBean的一些特色

通常如上声明后,@Bean注解若是不显式声明bean名称则方法名做为bean的名称,并且返回值做为注入的Bean。可是咱们经过debug发现倒是这样的:code

也就是说经过方法名是返回FactoryBean 建立的Bean。那么如何返回该FactoryBean呢?上图中也给出了答案在方法前增长引用符“&”。具体的缘由还用从BeanFactory中寻找,真是否是冤家不聚头

咱们对上面声明的两个bean进行测试,也出色地完成了不一样的定时任务业务逻辑。

@Autowired
    private Task customTask;
    @Autowired
    private Task otherTask;


    @Test
    public void task() {
        customTask.execute();
        otherTask.execute();
    }

4. 总结

在后续的使用中你能够经过声明不一样的cron表达式,以及不一样SomeService来定制更多的定时任务。经过这个例子相信你会对FactoryBean有的清晰的认识。demo就不提供了,很是简单,强烈建议你本身试一试以加深理解。

关注公众号:码农小胖哥 获取更多资讯

相关文章
相关标签/搜索