领先一步
VMware 提供培训和认证,助您加速进步。
了解更多得益于我们的可插拔理念以及实现上的大量努力,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 的名称就是方法的名称。可以通过注解定义额外的别名,但最好从方法名中获取名称,而不是从注解中获取,因为这意味着编译器可以确保名称的唯一性。
Bean 在 Java 代码中配置,使用构造函数、属性或任意方法调用。请注意,调用另一个 Bean 方法会建立从“book”Bean 到“rod”Bean 的依赖关系。但这与在没有框架支持的情况下在 Java 中实例化对象相比,具有关键优势:例如
将此与实现相同结果的 XML 定义进行比较,可能会更容易理解发生了什么,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 配置具有一些有趣的特点。例如:
<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 核心的未来版本中。
此外,它还需要一个响亮的名字。欢迎提出建议!
遗憾的是,自从那以后,我没有时间再对它进行更多的断断续续的开发,所以一直没有达到可以发布的程度。幸运的是,Spring Modules 的负责人兼 Spring 方面的全能专家 Costin Leau,自从今年早些时候加入 Interface21 以来,有更多的时间用于 Spring 编码,他已经挺身而出,推动这项工作。
该实现不需要对 Spring 核心进行任何修改。正如我所说,IoC 容器非常灵活。如果您有兴趣,它将配置对象视为一个工厂 Bean,并且每个 Bean 定义都由该对象上的实例工厂方法支持:这是自 Spring 1.1 以来就可用的机制。它对配置实例进行了一些字节码操作,目前使用 CGLIB,以确保对单例作用域的 @Bean 方法的重复调用始终返回相同的对象。