FactoryBean的使用方法及作用

FactoryBean 的使用方法

  1. 自定义 MyFactoryBean ,只需要实现 FactoryBean 接口即可
package com.jack.factoryBean;
@Component
public class MyFactoryBean implements FactoryBean {
	@Override
	public MyService getObject() throws Exception {
		return new MyService();
	}
	@Override
	public Class getObjectType() {
	    // FactoryBean所产生的Bean的类型
		return MyService.class;
	}
	@Override
	public boolean isSingleton() {
		// 设置返回的Bean是否为单例
		return true;
	}
}

我们需求将这个 MyFactoryBean 加入到IOC容器中,使用 @Component 注解即可

  1. 自定义一个IOC容器,这里我们采用的是 AnnotationConfigApplicationContext,快速构建一个IOC容器,并将上面的Bean扫描到IOC容器中
@Configuration
@ComponentScan("com.jack.factoryBean")
public class FactoryBeanConfig {

}

经过这个步骤,容器中应该有一个 MyFactoryBean ,并且名字为 myFactory,事实上并不是这样。

  1. 我们来看下面的测试类:
@Test
	public void testFactoryBean() {
		AnnotationConfigApplicationContext context =
				new AnnotationConfigApplicationContext(FactoryBeanConfig.class);
		Object bean1 = context.getBean("myFactoryBean");
		System.out.println("名字为myFactoryBean的bean是" + bean1.getClass());
		Object bean2 = context.getBean("&myFactoryBean");
		System.out.println("名字为&myFactoryBean的bean是" + bean2.getClass());
	}

执行结果为:
FactoryBean的使用方法及作用_第1张图片 是不是跟我们想象的有点不一样,使用 @Compontent 注入到容器中的类型是 MyFactoryBean,自动生成的名字是 myBeanFactory,但是事实上根据这个名字获取的的却是 MyFactoryBeangetObject() 方法所返回的实例。确实是这样,我们往容器中添加一个 FactoryBean (实现了FactoryBean接口的Bean),实际上在容器中产生了两个Bean,一个是这个 FactoryBeangetObject() 方法所返回的实例,这个Bean正好使用了自动生成的名字,另一个Bean则是真正的 FactoryBean 类型,而它的名字是在名字前面加一个 & 。总之,如果使用 FactoryBean ,那么会产生两个Bean。

FactoryBean 的作用

spring往IOC容器中添加Bean的方式有很多种,例如在xml中使用bean标签,使用 @Bean@Component 等注解,那为什么还要设计这么一种方式给容器中添加Bean呢?主要原因是为了服务第三方类。
我们在一个项目中除了使用Spring框架以外,可能还要用到其他很多第三方框架,例如 ORM 框架 mybatis ,为了使用IOC容器进行解耦,我们需要将第三方框架中的核心类加入到spring的IOC容器中去,那怎么加进去呢?我们不能修改第三方框架的代码,因此在类上加入 @Compontent 或其他注解肯定是不行的,于是我们还可以使用 bean 标签在xml文件中定义,这种方式行不通,原因是如果我们需要添加的 Bean有很多属性,则 bean标签的数量会让任何一个程序员崩溃。当然还可以在配置类中使用 @Bean 注解,自己new一个这个对象,然后加入到IOC容器中去,这种方式固然可以,但是局限也很大,此时的IOC容器需要是 AnnotationConfigApplicationContext 这种类型,如果我们还想使用 @Component 或者 @Service 等注解灵活在添加到容器中,就必须借助 FactoryBean,其实使用起来也非常简单,就如上面的例子,FactoryBean 在各种框架中使用非常广泛。下面看看 Mybatis 是如何使用的。
我们知道使用 Mybatis 的时候需要有一个 SqlSessionFactory,将它添加到容器中,可以让spring帮助 Mybatis 管理 sqlSessionmapper, 但是 SqlSessionFactory 的属性又非常多,正好面临上面说的困境,于是 mybatismybatis-spring 框架包中提供了一个 SqlSessionFactoryBean 给开发者使用,部分代码如下:

public class SqlSessionFactoryBean implements FactoryBean, InitializingBean, ApplicationListener {

  private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);

  private Resource configLocation;

  private Configuration configuration;

  private Resource[] mapperLocations;

  private DataSource dataSource;

  private TransactionFactory transactionFactory;

  private Properties configurationProperties;

  private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

  private SqlSessionFactory sqlSessionFactory;

  //EnvironmentAware requires spring 3.1
  private String environment = SqlSessionFactoryBean.class.getSimpleName();

  private boolean failFast;

  private Interceptor[] plugins;

  private TypeHandler[] typeHandlers;

  private String typeHandlersPackage;

  private Class[] typeAliases;

  private String typeAliasesPackage;

  private Class typeAliasesSuperType;

  //issue #19. No default provider.
  private DatabaseIdProvider databaseIdProvider;

  private Class vfs;

  private Cache cache;

  private ObjectFactory objectFactory;

  private ObjectWrapperFactory objectWrapperFactory;

  /**
   * Sets the ObjectFactory.
   *
   * @since 1.1.2
   * @param objectFactory
   */
  public void setObjectFactory(ObjectFactory objectFactory) {
    this.objectFactory = objectFactory;
  }

具体使用如下:

@Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setLogImpl(Log4jImpl.class);
        sqlSessionFactoryBean.setConfiguration(configuration);
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

于是我们可以自己创建一个 SqlSessionFactoryBean 并添加到IOC容器中,这样就可以将它生成的 SqlSessionFactory 也一并交由IOC容器管理了。当然我们需要的是 SqlSessionFactory,现在可以使用 @Autowired 等标签从容器中获取了。
这是 mybatis 提供的一种使用 FactoryBean 的思路,我们在使用IOC编程的时候,如果遇到类似的困难,也可以使用 FactoryBean 巧妙地解决。

你可能感兴趣的:(Spring源码学习)