Spring 的 Java 配置选项

工程 | Rod Johnson | 2006年11月28日 | ...

感谢我们可插拔的理念以及在实现方面付出的大量努力,Spring IoC 容器(就像 Spring 的大部分其他部分一样)非常灵活。

一个经常被忽略的点是,Spring 配置不需要在 XML 中,尽管 XML 格式是目前最常用的格式。Spring 拥有自己的内部元数据格式,形式为 BeanDefinition 接口及其子接口。表示 IoC 容器实例的 BeanFactory 和 ApplicationContext 实现由此 Java 元数据驱动,并且与元数据解析完全分开,元数据解析通常由 BeanDefinitionReader 实现执行。

BeanDefinition 元数据最初并非面向最终开发者设计的。在 Spring 2.0 中,NamespaceHandlers(处理 XML 扩展命名空间的类)生成 BeanDefinition 元数据,我们引入了 BeanDefinitionBuilder,它具有流畅的 API,使这更容易。但是,生成 BeanDefinition 元数据仍然属于基础设施编码的范畴,而不是在编写业务逻辑和定义常规 Spring bean 时每天都要做的事情。

今天我想描述一个在 Java 代码中定义 bean 的新选项,它针对最终用户(开发者)而不是基础设施提供商的。这目前是 Spring 核心附加组件的里程碑式版本,但可能会进入 Spring 正式版。

让我们从一个例子开始

@Configuration
public class MyConfig {
   @Bean
   public Person rod() {
      return new Person("Rod Johnson");
   }

   @Bean(scope = Scope.PROTOTYPE)
   public Book book() {
      Book book = new Book("Expert One-on-One J2EE Design and Development");
      book.setAuthor(rod());  // rod() method is actually a bean reference !
      return book;
   }
}

@Configuration 注解将此对象标识为特殊的配置类。每个 @Bean 方法都定义一个 bean。bean 的名称是方法的名称。可以使用注解定义其他别名,但是最好从方法中获取名称,而不是从注解中获取,因为这意味着编译器可以确保避免歧义。

使用构造函数、属性或任意方法调用在 Java 代码中配置 bean。请注意,对另一个 bean 方法的调用会建立从“book”bean 到“rod”bean 的依赖关系。但是,与在没有框架支持的情况下在 Java 中实例化对象相比,它具有关键优势:例如

  • 每个 @Bean 都是一个 Spring 组件,可以利用所有 Spring 服务,例如声明式事务管理。
  • 每个公共 @Bean 方法都会添加到 Spring 容器中,因此它有资格注入到其他对象中,进行 JMX 导出以及其他好处。
  • 它可以平滑地融入现有的 Spring 环境。

通过将其与 XML 定义进行比较以实现相同的结果,可能会更容易理解发生了什么,这将如下所示

<bean id="rod" class="Person" scope="singleton">
   <constructor-arg>Rod Johnson</constructor-arg>
</bean>

<bean id="book" class="Book" scope="prototype">
   <constructor-arg>Expert One-on-One J2EE Design and Development</constructor-arg>
   <property name="author" ref="rod"/>
</bean>

尽管它基于注解,但这种 Java 配置机制在我见过的注解用法中是独一无二的,因为注解不包含在核心业务逻辑中,而是在单独的配置类中。实际上,它是一种配置的 DSL。因此,它保留了 Spring 的非侵入性承诺:您不需要更改 Java 代码即可使用它。

配置类类似于 XML bean 定义文件,因此 @Configuration 注解包含一些与<beans>元素类似的选项,例如默认自动装配或延迟初始化。例如


@Configuration(defaultAutowire = Autowire.BY_TYPE, defaultLazy = Lazy.FALSE)
public class DataSourceConfiguration  extends ConfigurationSupport {
}

@Bean 注解允许设置范围和延迟初始化等选项,就像<bean>元素一样。默认范围是 Singleton,与 XML 一样。

这种风格的 Java 配置具有一些有趣的特性。例如

  • 引用(例如示例中对“rod”bean 的引用)可以在重构后继续存在;任何好的 IDE 都提供强大的工具支持。
  • <li>Because configurations are Java classes, they can participate in inheritance relationships. For example, you could define a superclass that demands some abstract @Beans to be implemented in subclasses.</li>
    <li>It creates a new visibility option. An @Bean method can be protected, it which case it benefits from the usual characteristics of the Spring component, but is not visible externally--that is it not injectable and cannot be obtained by calling getBean() on the IoC context.</li>
    

我向人们展示这一点(一年多了)的经验是,他们有时需要花一点时间来理解它,但通常最终会非常热情。

并非旨在替代 Spring 的 XML 格式。就像 Spring 2.0 扩展命名空间一样——以及自 Spring 1.0 以来一直可用的属性文件的使用——它是一个附加选项。复杂的应用程序需要多种类型的配置,Spring 旨在为配置提供最佳的整体解决方案。我们将继续探索其他形式的配置。

您通常会混合使用 Java 和 XML 配置。您可以在同一个应用程序上下文中使用任意数量的 Java 配置类。

以下示例使用 XML bean 定义来定义如上所示的 MyConfig bean,它可以像任何普通 bean 一样注入。ConfigurationPostProcessor 处理所有带有 @Configuration 注解的 bean,生成必要的 bean 定义。

<beans>

 <bean class="..MyConfig"/>


 <bean class="org.springframework.beans.factory.java.ConfigurationPostProcessor"/>
 
 <bean class="SomeRandomBean">
 	<property...
 </bean>
</beans>

当然,您可以在同一个 XML 中拥有普通的 bean 定义,例如此示例中的“SomeRandomBean”。您可以从 Java 配置和现有 XML 配置构建上下文。

Costin 还实现了一个方便的应用程序上下文,它使用通配符从类路径加载类,如下所示

ApplicationContext oneConfig = new  AnnotationApplicationContext(SimpleConfiguration.class.getName());
ApplicationContext aBunchOfConfigs = new AnnotationApplicationContext("**/configuration/*Configuration.class");

使用 ASM 检查类,无需加载它们。在未来的版本中,我们可能会提供其他自动检测方案。

该版本在此处。Costin Leau 现在是项目负责人。代码应视为 alpha 质量。我们包含了修改后的 Spring Pet Store 示例,但是无疑将从在实际项目中的使用中吸取教训。

Costin 和我都非常希望收到您对此功能的反馈。

这段代码将会怎样呢?这取决于您。它肯定需要反馈(欢迎提出建议),并且各种可能性(以及实现改进)将通过在实际应用中使用任何技术而出现。它目前不在路线图上,但是如果它激发了足够的兴趣,可能会进入未来版本的 Spring 核心。

此外,它需要一个简洁的名称。欢迎提出建议!


虽然今天是第一次(alpha)发布,但此功能的历史出奇地长——超过一年。2005年8月,在科罗拉多州克雷斯特德比特参加软件峰会时,我和 Spring.NET 项目的 Mark Pollack 和 Aleks Seovic 疯狂地编写代码。我记得在 Aleks 从大沙丘开车到丹佛的时候,我在捷豹 XJ8 的后座写了很多代码。我可能需要编写代码来转移我对危险的注意力。我认为这个想法的起源实际上可以追溯到 2005 年 JavaOne 上与 Howard Lewis Ship 的一次谈话……

遗憾的是,自从那时起,我没有时间做更多的事情,只是断断续续地进行这项工作,所以还没有达到我们可以发布的程度。幸运的是,Spring Modules 负责人兼 Spring 专家 Costin Leau 自今年年初加入 Interface21 以来,有更多时间进行 Spring 编码,并且他已挺身而出,将这项工作向前推进。

实现不需要对 Spring 核心进行任何修改。正如我所说,IoC 容器非常灵活。如果您感兴趣,它将配置对象视为工厂 bean,并且每个 bean 定义都由该对象上的实例工厂方法支持:自 Spring 1.1 以来一直可用的机制。它对配置实例进行一些字节码操作,目前使用 CGLIB,以确保对单例范围的 @Bean 方法的重复调用始终返回相同的对象。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获得支持

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

了解更多

即将举行的活动

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

查看全部