首页 > 其他分享 >Spring FactoryBean接口

Spring FactoryBean接口

时间:2022-12-04 17:44:49浏览次数:47  
标签:BeanFactory Spring 接口 getObject Bean FactoryBean Class

说明:

  1. 本文基于Spring-Framework 5.1.x版本讲解

  2. 建议读者对Mybatis有基本的使用经验

 

概述

这一篇我们讲讲org.springframework.beans.factory.FactoryBean接口,这个接口功能非常强大,可以集成不同的中间件或组件到Spring容器中来,可以说该接口是打通Spring与外界沟通的重要桥梁,是Spring非常重要的一个拓展点。 不少人会拿BeanFactoryFactoryBean做比较,其实这两个接口根本就没有可比性,完全不是一个’层次‘的产物。废话不多说,让我们开始吧。

 

FactoryBean

先看下FatoryBean接口在源码中的定义

/**
 * Interface to be implemented by objects used within a {@link BeanFactory} which
 * are themselves factories for individual objects. If a bean implements this
 * interface, it is used as a factory for an object to expose, not directly as a
 * bean instance that will be exposed itself.
 *
 * <p><b>NB: A bean that implements this interface cannot be used as a normal bean.</b>
 * A FactoryBean is defined in a bean style, but the object exposed for bean
 * references ({@link #getObject()}) is always the object that it creates.
 *
 * <p>FactoryBeans can support singletons and prototypes, and can either create
 * objects lazily on demand or eagerly on startup. The {@link SmartFactoryBean}
 * interface allows for exposing more fine-grained behavioral metadata.
 *
 * <p>This interface is heavily used within the framework itself, for example for
 * the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
 * {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
 * custom components as well; however, this is only common for infrastructure code.
 *
 * <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not
 * supposed to rely on annotation-driven injection or other reflective facilities.</b>
 * {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the
 * bootstrap process, even ahead of any post-processor setup. If you need access to
 * other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
 *
 * <p><b>The container is only responsible for managing the lifecycle of the FactoryBean
 * instance, not the lifecycle of the objects created by the FactoryBean.</b> Therefore,
 * a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()}
 * will <i>not</i> be called automatically. Instead, a FactoryBean should implement
 * {@link DisposableBean} and delegate any such close call to the underlying object.
 *
 * <p>Finally, FactoryBean objects participate in the containing BeanFactory's
 * synchronization of bean creation. There is usually no need for internal
 * synchronization other than for purposes of lazy initialization within the
 * FactoryBean itself (or the like).
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 08.03.2003
 * @param <T> the bean type
 * @see org.springframework.beans.factory.BeanFactory
 * @see org.springframework.aop.framework.ProxyFactoryBean
 * @see org.springframework.jndi.JndiObjectFactoryBean
 */
public interface FactoryBean<T> {

	T getObject() throws Exception;

	Class<?> getObjectType();

	default boolean isSingleton() {	return true; }

}

从接口描述信息中我们可以得到以下几点关键信息:

1. 实现了BeanFactory接口的Bean,实际上对外暴露的是getObject方法返回的对象;

2. FactoryBean支持创建单例和原型Bean,可以通过懒加载或容器启动时加载的方式创建Bean,可以实现SmartFactoryBean接口来控制更多的Bean创建方式;

3. Spring只会管理FactoryBean对象本身,通过FactoryBean#getObject创建出来的对象的生命周期则不会由Spring管理。 也就是说通过getObject返回的对象本身即使实现了生命周期接口,也不会被调用;

 

第2、3点不在我们的讨论范围之内,也比较好理解,我们把关注点放在第1点这上。 通过getObject方法来创建对象?  我直接用@Bean的方式不行吗? 其实是可以的,在创建简单对象的场景中,直接使用@Bean甚至比使用BeanFactory的方式更合理、更简单。 那BeanFactory的使用场景到底是什么?  如果实现一个需求,@BeanBeanFactory两种创建Bean的方式如何选择? 

提供下我的思路供参考:

1. 如果你已经明确了要创建的Bean对象的Class的时候,这时候直接使用@Bean即可,如果无法拿到Class,则使用BeanFactory的方式。例如,你作为一个框架编写者,创建哪些Bean往往是由上层的框架使用者来决定的,这种情况下使用@Bean肯定是不行的,所以一定要用BeanFactory

 2. 假设你可以拿到要创建的Bean对象的Class,但是不想暴露太多的Bean对象创建细节,如复杂的Bean创建过程,这种情况也可以使用BeanFactory 。但是什么才是复杂的Bean?如何不暴露创建细节?这个就需要你自己有非常足够多的说服力了。

所以个人认为,第一点才是决策的关键

 

我们在来简单解释下为什么说文中开始部分提到的BeanFactoryFactoryBean不是同一个层次的产物,相信你大概有所体会: BeanFactory 是Bean工厂、是容器,我们创建的Bean都在BeanFactory 中;而FactoryBean 只是容器创建Bean提供的一种方式而已, 可以说FactoryBean 就是为BeanFactory 服务的。 

好,我们下面再来举个Spring集成Mybatis的例子加深下对FactoryBean 的理解

 

Spring集成Mybatis

我们的业务代码中操作数据库,少不了使用@Resource、@Autowired等注解注入Mybatis的Mapper对象的场景, 这就说明这些Mapper对象的实例一定在Spring容器的管理之中,再往下想想,这些Mapper对象是如何被Spring纳入其中的? 其实就少不了 FactoryBean 接口的功劳, 想象一下你站在Mybatis框架实现者的角度,使用@Bean注解知否能实现?

我们看下框架实际是如何集成的,这里免不了贴一下源码,大家耐心看。 

 

一、我们要知道Mapper文件的位置, Spring才能绑我们扫描, 但是光扫描还不行,因为Spring只会把扫描出来的Class转换为beanDefinition并放在DefaultListableBeanFactory#beanDefinitionMap容器中,Spring就是根据beanDefinitionMap的内容来帮我们创建Bean的, 可是现阶段我们只有Mapper的Class,直接把Mapper的Class放到beanDefinitionMap中,Spring只会创建该Class的实例,那结果肯定是无法实例化的。我们需要找到一种类似于’占位符‘的机制,先把这个’占位符‘放入beanDefinitionMap 中,在实际使用时,在通过’占位符‘把实际的Bean对象构建出来

这时候FactoryBean 就登场了,FactoryBean类似于上述中的’占位符‘, 由于FactoryBean 的特性,我们完全可以把FactoryBean 先注册到beanDefinitionMap中,在实际使用阶段,我们可以通过getObject方法从SqlSessionFactory中拿到Mybatis为我们创建好的的Mapper代理对象了

package org.mybatis.spring.mapper;
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
    
  private String sqlSessionFactoryBeanName;
  // MapperFactoryBean 实现了FactoryBean接口
  private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();
  /**
   * 实际扫描Mapper的的方法
   */
  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 扫描出来的所有Mapper的BeanDefinition集合
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    // 调用下面的方法对Mapper对应的BeanDefinition做处理
    processBeanDefinitions(beanDefinitions);
    return beanDefinitions;
  }
  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();        
      // 把Mapper的Class以构造方法的形式传进去
      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      //注意这里,是设置的MapperFactoryBean的Class,意味着Spring要帮我们创建MapperFactoryBean对象
      definition.setBeanClass(this.mapperFactoryBean.getClass());
      //给MapperFactoryBean对象种注入sqlSessionFactory对象,在getObject()方法会用
      definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
    }
  }
}

 

二、 到这里我们已经把MapperFactoryBean注册到beanDefinitionMap中了,后续我们在业务代码中注入Mapper的时候,实际注入的是MapperFactoryBean#getObject方法返回的代理对象,我们在来看下getObject的实现

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;
  
  // 有参构造方法的mapperInterface接口在上一步传入
  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  @Override
  public T getObject() throws Exception {
    // getSqlSession()实际就是通过我们的SqlSessionFactory对象调用openSession获取出来的
    return getSqlSession().getMapper(this.mapperInterface);
  }

  @Override
  public Class<T> getObjectType() {
    return this.mapperInterface;
  }
    
  @Override
  public boolean isSingleton() {
    return true;
  }
}

 好,到了这里相信你已经明白了Spring是如何集成Mybatis的了 

 

小结

这一篇主要讲了FactoryBean 的用法以及与BeanFactory的简单比较,说明了他们的区别,并且借助于Spring集成Mybatis的实际案例了解了FactoryBean 在实际中的用法,其实FactoryBean的身影无处不在,在AOP以及Spring与其他框架的集成中会经常用到。

在最后简单说明一下:在实际工作中FactoryBean 接口用的并不是很多,因为大部分我们都在实现业务需求,直接使用@Bean方式创建Bean即可; 如果你是一位中间件、框架开发者,亦或是为了代码的可拓展性实现一个功能,那FactoryBean 将是你的不二之选。 

 

标签:BeanFactory,Spring,接口,getObject,Bean,FactoryBean,Class
From: https://www.cnblogs.com/linyigg/p/16949933.html

相关文章

  • 【博学谷学习记录】超强总结,用心分享|狂野架构SpringBoot概念和依赖管理
    SpringBoot主要特性1、SpringBootStarter:他将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven或Gradle构建中;2、使编码变得简单,Spri......
  • 第一天springboot学习笔记
    1.controller里注解的写法 1.1基础写法@Controller//引用ControllerpublicclasshelloController{@RequestMapping(value="/users",method=RequestMethod.GE......
  • Springboot之additional-spring-configuration-metadata.json自定义提示
    【3】@ConfigurationProperties注入属性https://blog.csdn.net/qq_25614773/article/details/124788923 https://docs.spring.io/spring-boot/docs/2.4.7/reference/ht......
  • SpringBoot使用@Validation校验实体参数
    在做前后端分离时,需要提供接口给前端,那么接口的字段实体校验就显得尤为重要了在需要实体校验的实体类前加上@Validated@RequestBody注解是校验的实体类的字段上加......
  • Spring事务
    一、事务的概念对一组操作而言,要么全部成功,要么全部失败,就叫做事务。01特性原子性(Atomicity):一个事务中的所有操作,要么全部成功,要么全部失败,如果中间发生异常,则全部回......
  • 【Spring系列】- Spring循环依赖
    Spring循环依赖......
  • 5 类与对象&接口
    HeadFirstJava和AcWingJava课程做的总结5。5.0对象之母Object在Java中的所有类都是从Object这个类继承出来的。Object类是所有类的源头,它是所有类的父类。如果J......
  • 接口测试与自动化测试
    一、接口测试1、接口测试用例的编写要点有哪些?(问法二:接口测试用例设计需要考虑哪些方面?问法三:接口测试中有哪些要注意的测试点?)1)考虑输入参数和输出参数的合法性,参数必填......
  • Spring Boot3.0升级,踩坑之旅,附解决方案
    本文基于newbeemall项目升级SpringBoot3.0踩坑总结而来,附带更新说明:Spring-Boot-3.0-发布说明Spring-Boot-3.0.0-M5-发布说明一.编译报错,importjavax.servlet.*;......
  • SpringBoot集成数据传输加密
    前言近期在对开发框架安全策略方面进行升级优化,提供一些通用场景的解决方案,本文针对前后端数据传输加密进行简单的分享,处理流程设计如下图所示,本加密方法对原有项目兼容性......