摘 要 针对传统的JEE架构方案常常无法让人满意程序过于复杂难以测试和维护成本高根据企业实际需求本文探讨了一种轻量级的JEE应用框架Spring 它用更加轻量更加灵活的基础设施取代了EJB在此对Spring背后的反向控制原理和面向切面编程技术进行了比较深入研究并与传统实现进行对比显示了这种框架具有大大降低开发成本可测试等优点
关键词 Spring反向控制面向切面编程POJO依赖注入
引言
在JEE的整个发展历程中现在正是一个非常时刻从很多方面来说JEE都是一个伟大的成功它成功地在从前没有标准的地方建立了标准大大提升了企业级软件的开放程度并且得到了整个行业和开发者的广泛认可然而JEE在一些方面已经开始捉襟见肘JEE应用开发的成本通常很高JEE应用项目至少和从前的非JEE项目一样容易失败——如果不是更容易失败的话这样的失败率高得让人难以接受在这样的失败率之下软件开发几乎变成了碰运气而在JEE遭遇失败的场景中EJB通常都扮演着重要的角色因此JEE社群不断地向着更简单的解决方案更少使用EJB的方向发展[]然而每个应用程序都需要一些基础设施拒绝使用EJB并不意味着拒绝EJB所采用的基础设施解决方案那么如何利用现有的框架来提供这些基础设施服务呢伴随着这个问题的提出一个轻量级的JEE解决方案出现了这就是Spring Framework
Spring是为简化企业级系统开发而诞生的Spring框架为JEE应用常见的问题提供了简单有效的解决方案使用Spring你可以用简单的POJO(Plain Old Java Object)来实现那些以前只有EJB才能实现的功能这样不只是能简化服务器端开发任何Java系统开发都能从Spring的简单可测试和松耦合特征中受益可以简单的说Spring是一个轻量级的反向控制(IoC)和面向切面编程(AOP)容器框架[]Spring IoC借助于依赖注入设计模式使得开发者不用理会对象自身的生命周期及其关系而且能够改善开发者对JEE模式的使用Spring AOP借助于Spring实现的拦截器开发者能够实现以声明的方式使用企业级服务比如安全性服务事务服务等Spring IoC和 Spring ; AOP组合一起形成了Spring这样一个有机整体使得构建轻量级的JEE架构成为可能而且事实证明非常有效没有Spring IoC的Spring AOP是不完善的没有Spring AOP的Spring IoC是不健壮的本文是以Spring架构的成功的实际商务系统项目为背景阐述了反向控制原理和面向切面的编程技术在Spring框架中的应用同时抽取适量代码示意具体应用并和传统开发模式进行对比展示了Spring framework的简单高效可维护等优点
Spring IoC 反向控制原理
反向控制是Spring框架的核心但是反向控制是什么意思?到底控制的什么方面被反向了呢?年美国专家Martin Fowler发表了一篇论文《Inversion of Control Containers and the Dependency Injection pattern》阐述了这个问题他总结说是获得依赖对象的方式反向了根据这个启示他还为反向控制提出了一个更贴切的名字Dependency Injection(DI 依赖注入)
通常应用代码需要告知容器或框架让它们找到自身所需要的类然后再由应用代码创建待使用的对象实例因此应用代码在使用实例之前需要创建对象实例然而IoC模式中创建对象实例的任务交给IoC容器或框架(实现了IoC设计模式的框架也被称为IoC容器)使得应用代码只需要直接使用实例这就是IoC相对IoC 而言依赖注入的确更加准确的描述了这种设计理念所谓依赖注入即组件之间的依赖关系由容器在运行期决定形象的来说即由容器动态的将某种依赖关系注入到组件之中
IoC在Spring中的实现
任何重要的系统都需要至少两个相互合作的类来完成业务逻辑通常每个对象都要自己负责得到它的合作(依赖)对象你会发现这样会导致代码耦合度高而且难于测试使用IoC对象的依赖都是在对象创建时由负责协调系统中各个对象的外部实体提供的这样使软件组件松散连接成为可能下面示意了Spring IoC 应用步骤如下
()定义Action接口并为其定义一个execute方法以完成目标逻辑多年前GoF在《Design PatternElements of Reusable ObjectOriented Software》一书中提出Programming to an Interfacenot an implementation的原则这里首先将业务对象抽象成接口正是为了实施这个原则
()类UpperAction实现Action接口在此类中定义一个String型的域message并提供相应的setter和getter方法实现的execute方法如下
public String execute (String str) {
return (getMessage () + str)toUpperCase () ;
}
()编写Spring配置文件(beanxml)
<beans>
<bean id=TheAction class=netchenspringqsUpperAction>
<property name=message>
<value>HeLLo</value>
</property>
</bean>
</beans>
()测试代码
public void testQuickStart () {
ApplicationContext ctx=new
FileSystemXmlApplicationContext (beanxml);
Action a= (Action) ctxgetBean (TheAction);
Systemoutprintln (a execute (Rod Johnson));
}
上面的测试代码中我们根据beanxml创建了一个ApplicationContext实例并从此实例中获取我们所需的Action实现运行测试代码我们看到控制台输出
……
HELLO ROD JOHNSON
仔细观察一下上面的代码可以看到
()我们的组件并不需要实现框架指定的接口因此可以轻松的将组件从Spring中脱离甚至不需要任何修改这在基于EJB框架实现的应用中是难以想象的
()组件间的依赖关系减少极大改善了代码的可重用性Spring的依赖注入机制可以在运行期为组件配置所需资源而无需在编写组件代码时就加以指定从而在相当程度上降低了组件之间的耦合
Spring给我们带来了如此这般的好处那么反过来让我们试想一下如果不使用Spring框架回到我们传统的编码模式情况会是怎样呢?
首先我们必须编写一个配置文件读取类以实现Message属性的可配置化
其次得有一个Factory模式的实现并结合配置文件的读写完成Action的动态加载于是我们实现了一个ActionFactory来实现这个功能
public class ActionFactory {
public static Action getAction (String actionName) {Properties pro = new Properties ();
try {
proload (new FileInputStream (configproperties));
String actionImplName =(String)proget(actionName);
String actionMessage =(String) proget (actionName+_msg);
Object obj =ClassforName (actionImplName)newInstance ();
BeanUtilssetProperty(objmessageactionMessage);
return (Action) obj;
} catch (FileNotFoundException e) {
……
}
}
配置文件则采用properties文件形式如下所示
TheAction=netchenspringqsUpperAction
TheAction_msg=HeLLo
测试代码也作相应修改现在不论实现的好坏总之通过上面新增的多行代码终于实现了类似的功能如果现在有了一个新的需求这样这个ActionFactory每次都新建一个类的实例显然这对系统性能不利考虑到我们的两个Action都是线程安全的修改一下ActionFactory保持系统中只有一个Action实例供其它线程调用另外Action对象创建后需要做一些初始化工作修改一下ActionFactory使其在创建Action实例之后随即就调用Actioninit方法执行初始化Action的处理这样就差不多了下面我们来看看另外一个Factory
……
往往这些系统开发中最常见的需求会导致我们的代码迅速膨胀而Spring IoC的出现则大大缓解了这样的窘境通过以上实例可以看出Spring IoC为我们提供了如下几方面的优势
()应用组件不需要在运行时寻找其协作者因此更易于开发和编写应用
()由于借助于IoC容器管理组件的依赖关系使得应用的单元测试和集成测试更利于展开
()通常在借助于IoC容器关系业务对象的前提下很少需要使用具体IoC容器提供的API这使得集成现有的遗留应用成为可能
因此通过使用IoC能够降低组件之间的耦合度最终能够提高类的重用性利于测试而且更利于整个产品或系统集成和配置