Spring-中的-BeanFactory-与-FactoryBean

10次阅读

共计 3476 个字符,预计需要花费 9 分钟才能阅读完成。

1. 前提概要

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

2.BeanFactory

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

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

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 的使用场景。

3.2 构建一个 FactoryBean

我们声明定时任务一般具有下列要素:

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

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

  • 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。


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 容器中去。

@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 发现却是这样的:

也就是说通过方法名是返回 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 就不提供了,非常简单,强烈建议你自己试一试以加深理解。

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

正文完
 0