取得领先
VMware 提供培训和认证,以加速您的进步。
了解更多我在我之前的文章中了解了基本的 FactoryBean
是什么。虽然 FactoryBeans
很重要 - 并且了解它们的作用可以帮助您更有效地浏览框架 - 但从 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 配置方法中完成,不是吗?让我们回顾一下上一篇博文中的示例,一个自定义的 FactoryBean
,旨在工厂化 Car
。
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 在很多地方还提供了 Builder 作为 FactoryBean
的替代方案。作为一种模式,Builder 在概念上与 FactoryBean
相似。但是,在实践中,它们通常像上面演示的 CarBuilder
那样公开。它们通常是可链接的 - 一个方法返回 this
,因此后续调用不需要取消引用该对象,它们可以继续链接调用。此外,Builder 通常会执行我在上一个代码中强制执行的空指针检查。因此,正确重写的 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 ();
}
一个很好的例子是,与 Spring 的 FactoryBean
相比,构建器在 3.1 中提供了更流畅的体验,即新的 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
(当然仍然存在)现在委托给这些构建器类!