likes
comments
collection
share

FactoryBean使用及真实应用场景

作者站长头像
站长
· 阅读数 80

一、FactoryBean是什么?

FactoryBean是spring提供的一个接口,可以通过它创建自定义对象工厂,将其注入spring容器后,会将其本身和其所要创建的bean一同注入容器。他和工厂模式中的工厂本质上来没有太大的区别,只不过一个是由spring 容器管理,一个是使用者自己管理。FactoryBean中有三个方法如下:



public interface FactoryBean<T> {

	/**
	 * The name of an attribute that can be
	 * {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
	 * {@link org.springframework.beans.factory.config.BeanDefinition} so that
	 * factory beans can signal their object type when it can't be deduced from
	 * the factory bean class.
	 * @since 5.2
	 */
	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";


	/**
	 返回我们所要创建的bean
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory.
	 * <p>As with a {@link BeanFactory}, this allows support for both the
	 * Singleton and Prototype design pattern.
	 * <p>If this FactoryBean is not fully initialized yet at the time of
	 * the call (for example because it is involved in a circular reference),
	 * throw a corresponding {@link FactoryBeanNotInitializedException}.
	 * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
	 * objects. The factory will consider this as normal value to be used; it
	 * will not throw a FactoryBeanNotInitializedException in this case anymore.
	 * FactoryBean implementations are encouraged to throw
	 * FactoryBeanNotInitializedException themselves now, as appropriate.
	 * @return an instance of the bean (can be {@code null})
	 * @throws Exception in case of creation errors
	 * @see FactoryBeanNotInitializedException
	 */
	@Nullable
	T getObject() throws Exception;

	/**
	返回我们要创建bean的class类型
	 * Return the type of object that this FactoryBean creates,
	 * or {@code null} if not known in advance.
	 * <p>This allows one to check for specific types of beans without
	 * instantiating objects, for example on autowiring.
	 * <p>In the case of implementations that are creating a singleton object,
	 * this method should try to avoid singleton creation as far as possible;
	 * it should rather estimate the type in advance.
	 * For prototypes, returning a meaningful type here is advisable too.
	 * <p>This method can be called <i>before</i> this FactoryBean has
	 * been fully initialized. It must not rely on state created during
	 * initialization; of course, it can still use such state if available.
	 * <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
	 * {@code null} here. Therefore, it is highly recommended to implement
	 * this method properly, using the current state of the FactoryBean.
	 * @return the type of object that this FactoryBean creates,
	 * or {@code null} if not known at the time of the call
	 * @see ListableBeanFactory#getBeansOfType
	 */
	@Nullable
	Class<?> getObjectType();

	/**
	是否为单例
	 * Is the object managed by this factory a singleton? That is,
	 * will {@link #getObject()} always return the same object
	 * (a reference that can be cached)?
	 * <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
	 * the object returned from {@code getObject()} might get cached
	 * by the owning BeanFactory. Hence, do not return {@code true}
	 * unless the FactoryBean always exposes the same reference.
	 * <p>The singleton status of the FactoryBean itself will generally
	 * be provided by the owning BeanFactory; usually, it has to be
	 * defined as singleton there.
	 * <p><b>NOTE:</b> This method returning {@code false} does not
	 * necessarily indicate that returned objects are independent instances.
	 * An implementation of the extended {@link SmartFactoryBean} interface
	 * may explicitly indicate independent instances through its
	 * {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
	 * implementations which do not implement this extended interface are
	 * simply assumed to always return independent instances if the
	 * {@code isSingleton()} implementation returns {@code false}.
	 * <p>The default implementation returns {@code true}, since a
	 * {@code FactoryBean} typically manages a singleton instance.
	 * @return whether the exposed object is a singleton
	 * @see #getObject()
	 * @see SmartFactoryBean#isPrototype()
	 */
	default boolean isSingleton() {
		return true;
	}

}

二、使用

在使用FactoryBean的时候,大多数是于spring提供的其他接口一起使用

1.创建我们需要的bean及其builder

代码如下(示例):

@Data
public class Customize {

    private String id;

    private String name;

    private int age;

    private String address;

    private String phone;
}

public class CustomizeBuilder {

    private Customize customize;

    public CustomizeBuilder builder() {
        customize = new Customize();
        return this;
    }

    public CustomizeBuilder id(String id) {
        customize.setId(id);
        return this;
    }

    public CustomizeBuilder name(String name) {
        customize.setName(name);
        return this;
    }

    public CustomizeBuilder age(int age) {
        customize.setAge(age);
        return this;
    }

    public CustomizeBuilder address(String address) {
        customize.setAddress(address);
        return this;
    }

    public CustomizeBuilder phone(String phone) {
        customize.setPhone(phone);
        return this;
    }

    public Customize build() {
        return customize;
    }
}

2.创建FactoryBean的实例

实现了InitializingBean 的 afterPropertiesSet()方法,该方法将在实现类的属性加载完毕后调用。 代码如下(示例):

@Component
public class CustomizeFactoryBean implements FactoryBean<Customize> , InitializingBean {

    private Customize customize;

    @Override
    public Customize getObject() throws Exception {
        return customize;
    }

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

    @Override
    public void afterPropertiesSet() throws Exception {
        if (customize == null){
            customize= new CustomizeBuilder().builder()
                    .id("1")
                    .name("sun")
                    .age(18)
                    .address("beijing")
                    .phone("123456789")
                    .build();
        }
    }
}


之后我们在测试类中进行测试

@SpringBootTest
class SpringBeanApplicationTests {
    @Autowired
    private Customize customize;
    @Autowired
    private CustomizeFactoryBean customizeFactoryBean;
    @Test
    public void contextLoads() throws Exception {
        System.out.println(customizeFactoryBean);
        System.out.println(customizeFactoryBean.getObject());
        System.out.println(customize);
    }
}

输出: com.agp.springbean.factorybean.CustomizeFactoryBean@5a592c70 Customize(id=1, name=sun, age=18, address=beijing, phone=123456789) Customize(id=1, name=sun, age=18, address=beijing, phone=123456789)

对springboot进行调试得到注入FactoryBean工厂得到的默认对象就是其要创建的对象,可以通过&+factoryBeanName得到真实的bean工厂 FactoryBean使用及真实应用场景 FactoryBean使用及真实应用场景

整合mybatis

上述我们虽然通过@Component 将工厂注入到spring容器中,但是他真正的用法却不是这样。因为想要注册一个bean我们可以通过很多种方式去完成。这种方法明显是比较麻烦的,官方也不推荐使用spring相关注解注入。下面来看看mybatis-spring是如何将spring整合mybatis的。

mybatis是如何运行的?

mybatis的核心组件是 SqlSession,我们所有的sql操作都要使用这个接口完成。sqlSession又是由SqlSessionFactory构建的。通过build方法创建出了SqlSessionFactory,即得到SqlSession。具体解析过程这里不做介绍

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      // 传入配置文件,创建一个XMLConfigBuilder类
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      // 分两步:
      // 1、解析配置文件,得到配置文件对应的Configuration对象
      // 2、根据Configuration对象,获得一个DefaultSqlSessionFactory
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
      }
    }
  }

由上述代码可知,只要我们拥有了SqlSessionFactory就拥有了mybatis的所有能力,剩下的就是怎么把SqlSessionFactory集成到spring中 在mybatis-spring中有这样的一个Factorybean

public class SqlSessionFactoryBean
    implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ContextRefreshedEvent>{
	private SqlSessionFactory sqlSessionFactory;
....

  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
        "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }
    @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Class<? extends SqlSessionFactory> getObjectType() {
    return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
  }

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

通过了buildSqlSessionFactory()来创建了SqlSessionFactory(),具体细节不做介绍。相信到了这里在结合我们在使用spring结合mybatis时的配置,大家都应该能了解如何使用了,通过xml或者javaConfig的方式就SqlSessionFactoryBean加入到spring容器中将第三方的mybatis 集成到容器中,然后使用mybatis了

总结

FactoryBean主要是用来在spring初始化的过程中创建bean的工厂,个人理解它更像一个插件的接口。可以将第三方的类优雅的加载到spring容器中。可能还有其他功能有待开发

转载自:https://juejin.cn/post/7372914724512940059
评论
请登录