Spring简介
2015-09-06 16:29
633 查看
1、使用框架的意义与Spring的主要内容
随着软件结构的日益庞大,软件模块化趋势出现,软件开发也需要多人合作,随即分工出现。如何划分模块,如何定义接口方便分工成为软件工程设计中越来越关注的问题。良好的模块化具有以下优势:可扩展、易验证、易维护、易分工、易理解、代码复用。优良的模块设计往往遵守“低耦合高内聚”的原则。而“框架”是对开发中良好设计的总结,把设计中经常使用的代码独立出来,所形成的一种软件工具。用户遵守它的开发规则,就可以实现良好的模块化,避免软件开发中潜在的问题。广义上的框架无处不再,一个常见的例子就是PC硬件体系结构,人们只要按照各自需要的主板、显卡、内存等器件就可以任意组装成自己想要的电脑。而做主板的厂商不用关心做显卡厂商的怎么实现它的功能。软件框架也是如此,开发人员只要在Spring框架中填充自己的业务逻辑就能完成一个模块划分清晰纷的系统。
这里主要通过一个银行通知用户月收支记录的小例子来介绍轻型J2EE框架Spring的主要内容、它所解决的问题和实现的方法。
Spring框架主要可以分为3个核心内容:
1、容器
2、控制反转(IoC ,Inversion of Control)
3、面向切面编程(AOP ,Aspect-Oriented Programming)
例子中依次对这些特性进行介绍,描述了软件模块化后存在的依赖与问题,以及Spring框架如何解决这些问题。
2、一个简单的例子程序
假设有一个如下应用场景:(1)一个银行在每月的月初都需要向客户发送上个月的账单,账单发送的方式可以为纸质邮寄、或者短信方式。(2)还有一个潜在的需求:为了安全起见,在每个函数操作过程中都需要记录日志,记录参数传入是否正常,函数是否正常结束,以便出错时系统管理员查账。那么对这个需求进行简单实现。系统框图如下所示:
首先定义一个账单输出的接口:
?
?
?
3、Spring中的容器
A、模块化后出现的问题与隐患假设随着工程的复杂化,上面的例子需要分成两个模块,以便开发时分工,一般会以如下结构划分:
划分后再看原来的代码:
?
SMSReportGenerator()大量使用的话,修改就会十分复杂,一个声明没有修改就会出现大的BUG。
所以需要一种划分,让各个模块尽可能独立,当开发人员B修改自己的模块时,开发人员A不需要修改任何代码。
B、问题出现的原因
为例子中的程序画一个UML依赖图:
可以发现上述问题出现的原因主要是:模块A与模块B不但存在接口依赖,还存在实现依赖。ReportGenerator每次修改它的实现,都会对ReportService产生影响。那么需要重构消除这种实现依赖。
C、用容器解决问题
消除实现依赖一般可以通过添加一个容器类来解决。在例子程序容器代码如下:
?
?
reportGenertor = new SMSReportGenertor();改为ReportGenertor reportGenertor = new
MailReportGenertor();)即可,模块A不需要修改任何代码。一定程度上降低了模块之间的耦合。
4、Spring中的控制反转
A、还存在的耦合使用容器后模块A与模块B之间的耦合减少了,但是通过UML依赖图可以看出模块A开始依赖于容器类:
之前的模块A对模块B的实现依赖通过容器进行传递,在程序中用(ReportGenerator) Container.instance.getComponent(“reportGenerator”)的方法取得容器中SMSReportGenertor的实例,这种用字符(“reportGenerator”)指代具体实现类SMSReportGenertor 的方式并没有完全的解决耦合。所以在银行账单的例子中我们需要消除ReportService对容器Container的依赖。
B、控制反转与依赖注入
在我们常规的思维中,ReportService需要初始化它的属性private ReportGenerator reportGenerator就必须进行主动搜索需要的外部资源。不使用容器时,它需要找到SMSReportGenertor()的构造函数;当使用容器时需要知道SMSReportGenertor实例在容器中的命名。无论怎么封装,这种主动查找外部资源的行为都必须知道如何获得资源,也就是肯定存在一种或强或弱的依赖。那是否存在一种方式,让ReportService不再主动初始化reportGenerator,被动的接受推送的资源?
这种反转资源获取方向的思想被称为控制反转(IoC,Inversion of Control),使用控制反转后,容器主动地将资源推送给需要资源的类(或称为bean)ReportService,而ReportService需要做的只是用一种合适的方式接受资源。控制反转的具体实现过程用到了依赖注入(DI,Dependecncy Injection)的设计模式,ReportService类接受资源的方式有多种,其中一种就是在类中定义一个setter方法,让容器将匹配的资源注入:setter的写法如下:
?
?
C、Spring IoC容器的XML配置
每个使用Spring框架的工程都会用到容器与控制反转,为了代码复用,Spring把通用的代码独立出来形成了自己的IoC容器供开发者使用:
与上面例子中实现的容器相比,Spring框架提供的IoC容器要远远复杂的多,但用户不用关心Spring
IoC容器的代码实现,Spring提供了一种简便的bean依赖关系配置方式------使用XML文件,在上面的例子中,配置依赖关系只要在工程根目录下的“application.xml”编辑如下内容:
?
5、Spring中的面向切面编程
A、日志问题以及延伸在例子的需求中有一条是:需要记录日志,以便出错时系统管理员查账。回顾例子中的代码,在每个方法中都加了日志操作:
?
与日志输出相似的问题在编程中经常遇到,这种跨越好几个模块的功能和需求被称为横切关注点,典型的有日志、验证、事务管理等。
横切关注点容易导致代码混乱、代码分散的问题。而如何将很切关注点模块化是本节的重点。
B、代理模式
传统的面向对象方法很难实现很切关注点的模块化。一般的实现方式是使用设计模式中的代理模式。代理模式的原理是使用一个代理将对象包装起来,这个代理对象就取代了原有对象,任何对原对象的调用都首先经过代理,代理可以完成一些额外的任务,所以代理模式能够实现横切关注点。
可能在有些程序中有很多横切关注点,那么只需要在代理外再加几层代理即可。以银行账单为例介绍一个种用Java Reflection API动态代理实现的横切关注点模块化方法。系统提供了一个InvocationHandler接口:
?
?
?
C、Spring AOP 使用方法
Spring AOP使用中需要为横切关注点(有些时候也叫切面)实现一个类,银行账单的例子中,切面的实现如下:
?
实现完切面类后,还需要对Spring工程中的application.xml进行配置以便实现完整的动态代理:
?
这3个主要是声明XML中用于AOP的一些标签, <bean class="bank.LogAspect" /> 是在容器中声明LogAspect切面,<aop:aspectj-autoproxy />用于自动关联很切关注点(LogAspect)与核心关注点(SMSReportGenerator,ReportService)。不难发现Spring AOP的方法实现横切关注点得模块化要比用Java Reflection API简单很多。
6、Spring总结
银行月账单报表例子通过使用Spring框架后变成了如下结构:在Spring框架的基础上原来存在耦合的程序被分成松耦合的三个模块。无论那个模块修改,对其他模块不需要额外改动。这就完成了一种良好的架构,使软件易理解,模块分工明确,为软件的扩展、验证、维护、分工提供了良好基础。这就是Spring框架作用。当然Spring除了容器、控制反转、面向切面之外还有许多其他功能,但都是在这三个核心基础上实现的。
通过Spring框架来重构银行月账单例子的源代码在附录的“BankSpring”中。
相关文章推荐
- java获取上周所有日期
- 转:在Spring框架下配置JPA
- 搭建Java开发环境
- java.lang.String 类的所有方法
- JAVA实现电话本管理系统
- spring mvc 定义拦截器
- spring事务使用+常见出错解决方案
- ubuntu如何完全卸载Java
- Java程序运行机制
- spring注解总结
- log4j中Spring控制台输出Debug级信息过多解决方法
- 多线程-------java版
- 关于Spring集成Quartz的concurrent属性
- Java中的深拷贝(深复制)和浅拷贝(浅复制)
- 关于Spring集成Quartz的concurrent属性
- java并发编程中CountDownLatch和CyclicBarrier的使用
- java代理的学习,通过类实现接口来实现代理。proxy来创建动态类,和InvocationHandler接口的实现,和工作原理。
- Spring定时任务的几种实现
- Java SecureRandom的合理使用
- MyEclipse部署Jboss出现java.lang.OutOfMemoryError: PermGen space