Spring 中 FactoryBean 的使用

栏目: Java · 发布时间: 6年前

内容简介:许久没记录笔记了,这回来重新熟悉一下 Spring 中 FactoryBean 的使用,顾名思义,它是用来获得相应 Bean 的工厂的。它与另一个 Spring 中的接口 BeanFactory 的作用不一样的,不能多说了。FactoryBean 和 BeanFactory 都是在这儿说的是第一个 FactoryBean, 它的接口声明是它最终的效果是,Spring 容器中注册一个名称为 abcFactoryBean 的

许久没记录笔记了,这回来重新熟悉一下 Spring 中 FactoryBean 的使用,顾名思义,它是用来获得相应 Bean 的工厂的。它与另一个 Spring 中的接口 BeanFactory 的作用不一样的,不能多说了。FactoryBean 和 BeanFactory 都是在 org.springframework.beans.factory 包中,谁能一看类名搞清楚它们的差别?

ApplicationContext

这儿说的是第一个 FactoryBean, 它的接口声明是

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}

它最终的效果是,Spring 容器中注册一个名称为 abcFactoryBean 的 AbcFactoryBean 实例,通后名称 abcFactoryBean 获得的实际上是相应  AbcFactoryBean.getObject() 返回的对象,类型为 getObjectType() , isSingleton() 是否是单例。

原本不太想细究它,由于看到了 FactoryBean 下的子子孙孙们,意识到在以后的 Spring 应用中还是大有文章可做。下面是在一个最基本的 SpringBoot 项目中的 FactoryBean 的所有实现类

Spring 中 FactoryBean 的使用

例如其中的 ThreadPoolExecutorFactoryBean , ForkJoinPoolFactoryBean 可用于便利的创建线程池, ServiceLocatorFactoryBean 用于查找 Bean 的。以后如果想要某一个具体的 Bean 声明起来可能麻烦,这时候可以查阅一下是否有相应的 FactoryBean,配置会更简单些。

看到这里后,还可能是不知所以,下面来看个实际的例子,分几步:

实际要的 Bean 实现(Sender)

在应用中实际需要一个 Sender 实例,但我们不直接把它声明为一个 Spring 的 Bean

package yanbin.blog;
 
//这里没有 @Name, @Component 之类的注册用于声明为一个 Spring Bean
public class Sender {
 
    private String receiver;
 
    public Sender(String receiver) {
        this.receiver = receiver;
    }
 
    public void send() {
        System.out.println("Send message to " + receiver);
    }
}

着重强调一下,在使用 FactoryBean 时,实际的 Bean 实现(这里的 Sender) 不需要显式的注册到 Spring 上下文中,它的实例会由相应的 FactoryBean 注册的。

Sender 的 FactoryBean 实现 SenderFactoryBean

加了 @Named 注解,根据 Spring Bean 默认命名规则,我们知道它会注册一个 senderFactory 的 Spring bean。

package yanbin.blog;
 
import org.springframework.beans.factory.FactoryBean;
 
import javax.inject.Named;
 
@Named
public class SenderFactoryBean implements FactoryBean<Sender> {
 
    private String receiver;
 
    public void setReceiver(String receiver) {
        this.receiver = receiver;
    }
 
    @Override
    public Sender getObject() throws Exception {
        return new Sender(receiver == null ? "Sun" : receiver);
    }
 
    @Override
    public Class<?> getObjectType() {
        return Sender.class;
    }
 
    @Override
    public boolean isSingleton() {
        return false;
    }
}

那么这个名为 senderFactoryBean 的 Spring bean 的类型就显得有些特别了。看以下的测试代码

客户端测试程序 DemoApplication

下面用代码来验证在 Spring 容器中类型分别为 SenderSenderFactory 的 Bean 到底是什么

package yanbin.blog;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
 
@SpringBootApplication
public class DemoApplication {
 
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
 
        context.getBeansOfType(Sender.class).forEach((beanName, object) ->
            System.out.println(beanName + "=> " + object));
 
        context.getBeansOfType(SenderFactory.class).forEach((beanName, object) ->
            System.out.println(beanName + "=> " + object));
 
        context.getBean(Sender.class).send();
    }
}

以上代码输出如下:

senderFactoryBean=> yanbin.blog.Sender@12591ac8  &senderFactoryBean=> yanbin.blog.SenderFactoryBean@38145825  Send message to Sun

senderFactory 的类型是一个 Sender 实例,它就是 SenderFactory.getObject() 返回的实例。而 &senderFactory 才是我们看似用 @Named 注册到 Spring 上下文的 SenderFactory 实例,此处, & 像是 C 中的取地址操作一般。也就是说,如果我们要在其他的 Spring Bean 中引用它,可以用以下方式指定名称

import org.springframework.beans.factory.annotation.Qualifier;
...
 
@Inject
@Qualifier("senderFactory")
private Sender sender;
 
@Resource(name = "&senderFactory")
private SenderFactory senderFactory;
 
public MailService(@Named("sender") Sender sender) {
    this.sender = sender;
}

如果觉得 senderFactory 名称对应的竟然是一个 Sender 实例而别扭,那么注册 SenderFactory 时可以指定名称为 sender , 如

@Named("sender")
public class SenderFactory implements FactoryBean<Sender> {
    ......

或者用 JavaConfig 配置时用下面的形式

@Bean(name = "sender")
public SenderFactoryBean senderFactoryBean() {
    SenderFactoryBean factory = new SenderFactoryBean();
    factory.setReceiver("Moon");
    return factory;
}

记住,AbcFactoryBean 在 Spring 中会返回它的 getObject() 对应的类型 Abc,所以声明的 FactoryBean 最好指定一个更有意义的名称。

使用 JavaConfig 时看下程序的输出(需要把 SenderFactoryBean 上的 @Name 注解去掉)

sender=> yanbin.blog.Sender@1df8da7a  &sender=> yanbin.blog.SenderFactoryBean@7486b455  Send message to Moon

这时看到 sender 对应的是 Sender 实例,而带 & 前缀的 &sender 是相应的 SenderFactoryBean 实例。一般来说我们不会直接用到这个 FactoryBean 实例,除非我们基于它再行配置,手动调用它 getObject() 方法来获得一个不同的实例。不过这种用法会比较危险,它可能会修改现有的 sender 对应实例的状态。

一个应用实例

有关于 Spring 的  FactoryBean 的内容就这么多了,最后来看一个应用 ThreadPoolExecutorFactoryBean 的例子

@Bean(name = "threadPool")
public ThreadPoolExecutorFactoryBean threadPoolExecutorFactoryBean() {
    ThreadPoolExecutorFactoryBean factory = new ThreadPoolExecutorFactoryBean();
    factory.setCorePoolSize(5);
    factory.setMaxPoolSize(5);
    factory.setQueueCapacity(50);
    factory.setThreadNamePrefix("kafka");
    factory.setDaemon(true);
    factory.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    return factory;
}

这样的话,在容器里我们就有了一个名为 threadPool 线程池实例。看到上面,基本 ThreadPoolExecutorFactoryBean 我们可以轻松的定制具有以下功能线程池

Executors.newFixedThreadPool(5)

其他的 FactoryBean 都值得发掘。

链接:

  1. How to use the Spring FactoryBean?

以上所述就是小编给大家介绍的《Spring 中 FactoryBean 的使用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Probabilistic Method Second Edition

The Probabilistic Method Second Edition

Noga Alon、Joel H. Spencer / Wiley-Blackwell / 2000 / $121.95

The leading reference on probabilistic methods in combinatorics-now expanded and updated When it was first published in 1991, The Probabilistic Method became instantly the standard reference on one......一起来看看 《The Probabilistic Method Second Edition》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具