FactoryBean 之外

工程 | Josh Long | 2011 年 8 月 10 日 | ...

我在我之前的文章中查看了基本的FactoryBean是什么。虽然FactoryBean很重要——了解它们的作用可以帮助您更有效地浏览框架——但从 Spring 3.0 和即将推出的 Spring 3.1 开始,它们在很大程度上不再是推荐的解决方法。

FactoryBean的全部意义在于隐藏对象的构建——要么是因为它非常复杂,要么是因为它不能简单地使用 Spring 容器使用的典型以构造函数为中心的的方法进行实例化(也许需要查找?也许需要静态注册方法?)Spring 还支持 XML 格式中的factory-method属性。Java 配置方法提供了概念上类似的(实际上,结果相同)替代方案,但具有更简洁、类型安全的替代方案。

Spring 3.0 引入了 Java 配置,允许您使用 Java 定义 Bean。例如,要使用 XML 在 Spring 中注册常规的javax.sql.DataSource,您很可能会委托给属性文件以获取敏感配置信息(如数据库密码),并使用 Spring 实例化javax.sql.DataSource,如下所示


<beans ...>
	<context:property-placeholder location = "ds.properties" />

	<bean id = "ds" class = "a.b.c.MySqlDataSource">
	  <property name = "user" value = "${ds.user}"/>
	  <property name = "password" value = "${ds.password}"/>
	</bean>
</beans>

这是一个简单的 Bean,可以自然地转换为 Java 配置。它看起来像这样

 
import a.b.c.* ;
	
@Configuration 
@PropertySource("ds.properties") 
public class MyConfiguration { 
    @Inject private Environment env ; 
	
    @Bean public MySqlDataSource ds(){ 
        MySqlDataSource ds = new MySqlDataSource () ; 
        ds.setUser( env.getProperty("ds.user") );
        ds.setPassword( env.getProperty("ds.password"));
        return ds; 
    }
}

这样做的优点是您可以在方法内部自由地执行任何操作。返回值是在 Spring 容器中注册的内容。方法的名称(ds)用于设置 Bean 的名称。您为正确构建此对象所做的任何事情都由您决定——您不再受限于 Spring 基于 XML 指定的内容所能实例化的内容。这更自然——使用 Java 比 XML 更容易保证没有错误。因此,请记住,Java 配置在代码行数方面大致相同,但功能更强大,并且在概念上更自然。

随着这些限制的解除,FactoryBean的价值开始降低。毕竟,如果FactoryBean所做的只是以一种新颖或独特的方式编码构建逻辑,那么就没有理由不能在 Java 配置方法中完成,对吧?让我们重新审视上一篇博文中提供的示例,一个旨在创建Car的自定义FactoryBean

	 
public class MyCarFactoryBean implements FactoryBean<Car> {
  private String make; 
  private int year ;
  public void setMake (String m) { this.make = m ; }
  public void setYear (int y) { this.year = y; }
  public Car getObject (){ 
    // wouldn't be a very useful FactoryBean 
    // if we could simply instantiate the object! 
    CarBuilder cb = CarBuilder.car();
	
    if (year!=0) cb.setYear (this.year);
    if (StringUtils.hasText (this.make)) cb.setMake ( this.make); 
    return cb.factory(); 
  }
  public Class<Car> getObjectType () { return Car.class ; } 
  public boolean isSingleton () { return false; }
} 

在此示例中,我们仅在有值要设置时有条件地设置值。因此,我们围绕它做了一些操作以确保我们有值。这段代码很丑陋,因为它有很多不同的执行路径,但它并不是特别新颖。我们是成年人,我们可以自己做这种事。让我们放弃FactoryBean,并简单地使用 Java 配置来替换它以定义Car。同样,我们碰巧知道我们需要什么配置,因此我们不必在代码中重复null检查。

	
@Bean public Car honda (){ 
	return CarBuilder.car()
  	  .setYear( 1984 )
	  .setMake("Honda")
	  .factory(); 
}

不错!我们不再需要复杂的FactoryBean,并且我们有一个可用的 Bean 定义。如果我们想使它可重用,我们也可以这样做,只需创建一个工厂方法,如下所示

	

// presumably exposed from some place where other configuration classes can reuse it.
public Car buildCar (int year, String make){ 
    CarBuilder cb = CarBuilder.car();
    if (year!=0) cb.setYear( year);
    if (StringUtils.hasText( make)) cb.setMake( make); 
    return cb.factory ();
}
...
// now the Spring definition itself is even simpler, <em>and</em> it's reusable!
@Bean public Car honda () {
	return buildCar(1984, "Honda") ;
}

在 Spring 3.1 中,在许多地方,Spring 还提供了一个构建器替代方案来代替FactoryBean。作为一种模式,构建器在概念上类似于FactoryBean。然而,在实践中,它们通常像上面演示的CarBuilder一样公开。它们通常是可链接的——方法返回this,因此后续调用不需要取消引用对象,它们可以继续链接调用。此外,构建器通常会执行我在之前代码中强制执行的空指针检查。因此,正确重写的CarBuilder对象用法可能如下所示

	

@Bean public Car honda (){ 
 return CarBuilder.car ()
  // doesn't matter if the parameters are null - 
  // it'll validate in the factory() method
  .setYear( 1984 )  
  .setMake( "Honda" )
  .factory ();
}

在 3.1 中,构建器提供比 Spring 的FactoryBean更流畅体验的一个很好的例子是新的 Hibernate 3 SessionFactoryBuilder,其用法如下所示

	
	
@Configuration  
@EnableTransactionManagment 
public class ServiceConfiguration {  
	
  @Bean public javax.sql.DataSource dataSource (){ ... }

  @Bean public SessionFactory hibernate3SessionFactory(){ 
     return new AnnotationSessionFactoryBuilder()
 	     .setDataSource(dataSource())
 	     // you could do this:
 	     //.setAnnotatedClasses( Customer.class, LineItem.class, Order.class )
 	     // or simply scan a package where your entities live
 	     .setAnnotatedPackages( Customer.class.getPackage().getName ()) 
 	     .buildSessionFactory ();
 }
}

实际上,等效的FactoryBean(当然仍然存在)现在委托给这些构建器类!

获取 Spring 简讯

通过 Spring 简讯保持联系

订阅

领先一步

VMware 提供培训和认证,以助您快速提升技能。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部