2.5. 需要改造的地方
JpetStore4.0的关键就在struts Action类和form bean类上,这也是其精华之一(虽然该实现方式是试验性,待扩充和验证),在此次改造中我们要保留下来,即控制层一点不变,表现层获取相应业务类的方式变了(要加载spring环境),其它保持不变。要特别关注的改动是业务层和持久层,幸运的是JpetStore4.0设计非常好,需要改动的地方非常少,而且由模式可循,如下:
1. 业务层和数据层用Spring BeanFactory机制管理。
2. 业务层的事务由spring 的aop通过声明来完成。
3. 表现层(form bean)获取业务类的方法改由自定义工厂类来实现(加载spring环境)。
3. JPetStore的改造
3.1. 改造后的架构
其中红色部分是要增加的部分,蓝色部分是要修改的部分。下面就让我们逐一剖析。
3.2. Spring Context的加载
为了在Struts中加载Spring Context,一般会在struts-config.xml的最后添加如下部分:
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml" />
</plug-in>
|
Spring在设计时就充分考虑到了与Struts的协同工作,通过内置的Struts Plug-in在两者之间提供了良好的结合点。但是,因为在这里我们一点也不改动JPetStore的控制层(这是JpetStore4.0的精华之一),所以本文不准备采用此方式来加载ApplicationContext。我们利用的是spring framework 的BeanFactory机制,采用自定义的工具类(bean工厂类)来加载spring的配置文件,从中可以看出Spring有多灵活,它提供了各种不同的方式来使用其不同的部分/层次,您只需要用你想用的,不需要的部分可以不用。
具体的来说,就是在com.ibatis.spring包下创建CustomBeanFactory类,spring的配置文件applicationContext.xml也放在这个目录下。以下就是该类的全部代码,很简单:
public final class CustomBeanFactory {
static XmlBeanFactory factory = null;
static {
Resource is = new
InputStreamResource( CustomBeanFactory.class.getResourceAsStream("applicationContext.xml"));
factory = new XmlBeanFactory(is);
}
public static Object getBean(String beanName){
return factory.getBean(beanName);
}
}
|
实际上就是封装了Spring 的XMLBeanFactory而已,并且Spring的配置文件只需要加载一次,以后就可以直接用CustomBeanFactory.getBean("someBean")来获得需要的对象了(例如someBean),而不需要知道具体的类。CustomBeanFactory类用于{耦合1}的解耦。CustomBeanFactory类在本文中只用于表现层的form bean对象获得service类的对象,因为我们没有把form bean对象配置在applicationContext.xml中。但是,为什么不把表现层的form bean类也配置起来呢,这样就用不着这CustomBeanFactory个类了,Spring会帮助我们创建需要的一切?问题的答案就在于form bean类是struts的ActionForm类!如果大家熟悉struts,就会知道ActionForm类是struts自动创建的:在一次请求中,struts判断,如果ActionForm实例不存在,就创建一个ActionForm对象,把客户提交的表单数据保存到ActionForm对象中。因此formbean类的对象就不能由spring来创建,但是service类以及数据层的DAO类可以,所以只有他们在spring中配置。所以,很自然的,我们就创建了CustomBeanFactory类,在表现层来衔接struts和spring。就这么简单,实现了另一种方式的{耦合一}的解耦。
3.3. 表现层
面分析到,struts和spring是在表现层衔接起来的,那么表现层就要做稍微的更改,即所需要的service类的对象创建上。以表现层的AccountBean类为例:原来的源代码如下
private static final AccountService accountService
= AccountService.getInstance();
private static final CatalogService catalogService
= CatalogService.getInstance();
|
改造后的源代码如下
private static final AccountService accountService
= (AccountService)CustomBeanFactory.getBean("AccountService");
private static final CatalogService catalogService
= (CatalogService)CustomBeanFactory.getBean("CatalogService");
|
其他的几个presentation类以同样方式改造。这样,表现层就完成了。关于表现层的其它部分如JSP等一概不动。也许您会说,没有看出什么特别之处的好处啊?你还是额外实现了一个工厂类。别着急,帷幕刚刚开启,spring是在表现层引入,但您发没发现:
presentation类仅仅面向service类的接口编程,具体"AccountService"是哪个实现类,presentation类不知道,是在spring的配置文件里配置。(本例中,为了最大限度的保持原来的代码不作变化,没有抽象出接口)。Spring鼓励面向接口编程,因为是如此的方便和自然,当然您也可以不这么做。
CustomBeanFactory这个工厂类为什么会如此简单,因为其直接使用了Spring的BeanFactory。Spring从其核心而言,是一个DI容器,其设计哲学是提供一种无侵入式的高扩展性的框架。为了实现这个目标,Spring 大量引入了Java 的Reflection机制,通过动态调用的方式避免硬编码方式的约束,并在此基础上建立了其核心组件BeanFactory,以此作为其依赖注入机制的实现基础。org.springframework.beans包中包括了这些核心组件的实现类,核心中的核心为BeanWrapper和BeanFactory类。