先行一步
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 还实现了一个方便的应用程序上下文,它使用通配符从 classpath 中加载类,如下所示
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 方法的重复调用始终返回同一个对象。