四. 在Spring中实现自配置
你需要进一步实现的是,使插件具有自配置能力。尽管Spring并不直接支持这种功能,但是,借助于它提供的一些工具,实现这一目标也是相当直接的。实现自配置的关键部分是BeanFactoryPostProcessor,这是一个Spring调用的接口(该调用应该是在所有配置被发现和加载到一个内存描述之后,但在创建实际的对象之前发生)。
通过使用BeanFactoryPostProcessor,你可以动态地把所有的bean组合到一起而不必修改原始的文件系统配置。下列代码是我的BeanFactoryPostProcessor实现的核心部分:PluginBeanFactoryPostProcessor(下载源码中提供了完整的类):
private String extensionBeanName;//经由spring设置(在此没有显示setter) private String propertyName;//经由spring设置(在此没有显示setter) private String pluginBeanName;//经由spring设置(在此没有显示setter) /* *(非Javadoc) *@请参考BeanFactoryPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory) */ public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory) throws BeansException { //找到我们希望修改的bean定义 BeanDefinition beanDef = beanFactory.getBeanDefinition(extensionBeanName); //在该bean定义中,查找它的属性并且发现我们将修改的具体属性。 MutablePropertyValues propValues = beanDef.getPropertyValues(); if ( !propValues.contains(propertyName)) throw new IllegalArgumentException("Cannot find property " + propertyName + " in bean " + extensionBeanName); PropertyValue pv = propValues.getPropertyValue(propertyName); //取出值定义(在我们的情况下,我们仅支持列表风格属性的更新) Object prop = pv.getValue(); if ( !(prop instanceof List)) throw new IllegalArgumentException("Property " + propertyName + " in extension bean " + extensionBeanName + " is not an instanceof List."); //把我们的bean参考添加到列表中。当Spring创建对象 // 并且把它们绑定到一起时,我们的bean现在准备好了. List l = (List) pv.getValue(); l.add(new RuntimeBeanReference(pluginBeanName)); }
|
下面展示了配置在Spring中看上去的样子。首先,在你的核心工程中定义扩展点-它是example.craps.Table的一个实例,其中它的两个属性(dice,players)配置以空列表。这是标准的Spring用法:
<beans> <bean id="extension-point.craps.table" class="example.craps.Table" init-method="init"> <property name="dice"> <list> </list> </property> <property name="players"> <list> </list> </property> </bean> </beans>
|
现在,你可以使用插件类连同它的Spring上下文(这将是自发现的)打包一个jar文件,并且你可以拥有一个类似如下的配置:
<beans> <bean id="real-dice" class="example.craps.RealDice" /> <bean class="platform.spring.PluginBeanFactoryPostProcessor"> <property name="extensionBeanName" value="extension-point.craps.table" /> <property name="propertyName" value="dice" /> <property name="pluginBeanName" value="real-dice" /> </bean> </beans>
|
在这个Spring配置中定义了一个example.craps.RealDice的实例,然后,它定义你的PluginBeanFactoryPostProcessor(它被配置以找到extension-point.craps.table bean)。这一实例还会把你的real-dice bean添加到craps表的dice属性中。
注意,这是本文中真正的焦点所在。这个到Spring的小扩展就是编写基于插件的组件的所有要求。注意,如果你删除这个包含该Spring上下文的jar文件,那么,你还要从extension-point.craps.table bean中“分离”你的bean。然后,把该jar添加回去,并且把它自己绑定到系统中的适当位置。