




已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
原文地址:/myriads/blog/37922Spring框架简介1、使用框架的意义与Spring的主要内容 随着软件结构的日益庞大,软件模块化趋势出现,软件开发也需要多人合作,随即分工出现。如何划分模块,如何定义接口方便分工成为软件工程设计中越来越关注的问题。良好的模块化具有以下优势:可扩展、易验证、易维护、易分工、易理解、代码复用。 优良的模块设计往往遵守“低耦合高内聚”的原则。而“框架”是对开发中良好设计的总结,把设计中经常使用的代码独立出来,所形成的一种软件工具。用户遵守它的开发规则,就可以实现良好的模块化,避免软件开发中潜在的问题。广义上的框架无处不再,一个常见的例子就是PC硬件体系结构,人们只要按照各自需要的主板、显卡、内存等器件就可以任意组装成自己想要的电脑。而做主板的厂商不用关心做显卡厂商的怎么实现它的功能。软件框架也是如此,开发人员只要在Spring框架中填充自己的业务逻辑就能完成一个模块划分清晰纷的系统。 这里主要通过一个银行通知用户月收支记录的小例子来介绍轻型J2EE框架Spring的主要内容、它所解决的问题和实现的方法。Spring框架主要可以分为3个核心内容: 1、容器 2、控制反转(IoC ,Inversion of Control) 3、面向切面编程(AOP ,Aspect-Oriented Programming) 例子中依次对这些特性进行介绍,描述了软件模块化后存在的依赖与问题,以及Spring框架如何解决这些问题。 2、一个简单的例子程序 假设有一个如下应用场景:(1)一个银行在每月的月初都需要向客户发送上个月的账单,账单发送的方式可以为纸质邮寄、或者短信方式。(2)还有一个潜在的需求:为了安全起见,在每个函数操作过程中都需要记录日志,记录参数传入是否正常,函数是否正常结束,以便出错时系统管理员查账。 那么对这个需求进行简单实现。系统框图如下所示: 首先定义一个账单输出的接口:/接口public interface ReportGenerator public void generate(String table) ; 实现“打印纸质账单”与“发送短信”两个具体功能:/账单报表实现类 public class PageReportGenerator implement ReportGenerator public void generate(String table) ( . ); /输出日志 .打印操作,以便工作人员邮递给客户 ( . ); /输出日志 /短信报表实现类 public class SMSReportGenerator implement ReportGenerator public void generate(String table) ( . ); .短信发送操作 ( . ); 上层业务逻辑对上个月的账目进行统计并调用接口产生纸质或者短信结果:/上层业务中的服务类 public class ReportService private ReportGenerator reportGenerator = new SMSReportGenerator(); public void generateMonthlyReport(int year, int month) ( . ); String statistics = null ; . reportGenerator.generate(statistics); 这个实现源代码请查看文章结尾附录中的BankOld。源代码中与例子中程序略有区别:由于使用log4j需要引用外部的包,并且需要写配置文件,为了方便,源代码中的日志输出用system.out.println()代替。3、Spring中的容器 A、模块化后出现的问题与隐患 假设随着工程的复杂化,上面的例子需要分成两个模块,以便开发时分工,一般会以如下结构划分: 划分后再看原来的代码: /上层业务中的服务类 public class ReportService private ReportGenerator reportGenerator = new SMSReportGenerator(); /隐患 public void generateMonthlyReport(int year, int month) . 在服务类有private ReportGenerator reportGenerator = new SMSReportGenerator();这么一行代码,ReportService类与SMSReportGenerator类不属于同一个模块,当开发人员B对内部实现进行修改时,由于存在依赖,开发人员A也要进行修改(比如之前喜欢短信收账单的客户感觉短信不够详细,希望以后改用邮件收账单,那么开发人员B需要实现一个MailReportGenerator类,在开发人员B修改代码时,开发人员A也需要改代码-声明部分修改)。如果系统庞大new SMSReportGenerator()大量使用的话,修改就会十分复杂,一个声明没有修改就会出现大的BUG。所以需要一种划分,让各个模块尽可能独立,当开发人员B修改自己的模块时,开发人员A不需要修改任何代码。 B、问题出现的原因 为例子中的程序画一个UML依赖图 可以发现上问题出现的原因主要是:模块A与模块B不但存在接口依赖,还存在实现依赖。ReportGenerator每次修改它的实现,都会对ReportService产生影响。那么需要重构消除这种实现依赖。 C、用容器解决问题 消除实现依赖一般可以通过添加一个容器类来解决。在例子程序容器代码如下: /容器类 public class Container public static Container instance; private Map components; public Container() component = new HashMap(); instance = this; ReportGenertor reportGenertor = new SMSReportGenertor(); components.put(“reportGenertor”, reportGenertor); ReportService reportService = new ReportService(); components.put(“reportService”, reportService); public Object getComponent(String id) return components.get(id); 使用容器后,模块A的ReportService的属性实现方法也发生了变化。/服务类变更,降低了耦合 public class ReportService /private ReportGenerator reportGenerator = new SMSReportGenerator(); private ReportGenerator reportGenerator = (ReportGenerator) Container.instance.getComponent(“reportGenerator”); public void generateMonthlyReport(int year, int month) . 这样的话,class都在容器中实现,使用者只需要在容器中查找需要的实例,开发人员修改模块B后(在模块中增加邮件报表生成类MailReportGenerator),只需要在容器类中修改声明(把ReportGenertor 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的写法如下: /为需要依赖注入的类加入一种被称为setter的方法 public class ReportService /*private ReportGenerator reportGenerator = (ReportGenerator) Container.instance.getComponent(“reportGenerator”); */ private ReportGenerator reportGenerator; public void setReportGenerator( ReportGenerator reportGenerator) this.reportGenerator = reportGenerator; public void generateMonthlyReport(int year, int month) . 在容器中把依赖注入: /容器类 public class Container . public Container ( ) component = new HashMap(); instance = this; ReportGenertor reportGenertor = new SMSReportGenertor(); components.put(“reportGenertor”, reportGenertor); ReportService reportService = new ReportService(); reportService.setReportGenerator(reportGenerator); /使用ReportService的setter方法注入依赖关系 components.put(“reportService”, reportService); . 这样一来ReportService就不用管SMSReportGenertor在容器中是什么名字,模块A对于模块B只有接口依赖,做到了松耦合。 C、Spring IoC容器的XML配置 每个使用Spring框架的工程都会用到容器与控制反转,为了代码复用,Spring把通用的代码独立出来形成了自己的IoC容器供开发者使用: 与上面例子中实现的容器相比,Spring框架提供的IoC容器要远远复杂的多,但用户不用关心Spring IoC容器的代码实现,Spring提供了一种简便的bean依赖关系配置方式-使用XML文件,在上面的例子中,配置依赖关系只要在工程根目录下的“application.xml”编辑如下内容: 是标准的XML头,xmlns引用的是一些命名空间,两个一般在工程中自动生成。后面的内容由用户输入,主要表示实例化SMSReportGenerator,实例化ReportService并把SMSReportGenerator的对象smsReportGenerator赋值给ReportService的属性reportGenerator,完成依赖注入。5、Spring中的面向切面编程 A、日志问题以及延伸 在例子的需求中有一条是:需要记录日志,以便出错时系统管理员查账。回顾例子中的代码,在每个方法中都加了日志操作: /服务类 public class ReportService . public void generateMonthlyReport(int year, int month) ( . ); /记录函数的初始状况参数等信息 String statistics = null ; . reportGenerator.generate(statistics); ( . ); /记录函数的执行状况与返回值 /凭条报表实现类 public class PageReportGenerator implement ReportGenerator public void generate(String table) ( . ); /记录函数的初始状况参数等信息 打印操作 ( . ); /记录函数的执行状况与返回值 可以看出在每个方法的开始与结尾都调用了日志输出,这种零散的日志操作存在着一些隐患,会导致维护的困难。比如日志输出的格式发送了变化,那么无论模块A还是模块B的程序员都要对每个方法每个输出逐条修改,极容易遗漏,造成日志输出风格的不一致。又比如不用Log4j日志输出工具更换其他工具,如果遗漏一个将会出现严重BUG。 与日志输出相似的问题在编程中经常遇到,这种跨越好几个模块的功能和需求被称为横切关注点,典型的有日志、验证、事务管理等。 横切关注点容易导致代码混乱、代码分散的问题。而如何将很切关注点模块化是本节的重点。 B、代理模式 传统的面向对象方法很难实现很切关注点的模块化。一般的实现方式是使用设计模式中的代理模式。代理模式的原理是使用一个代理将对象包装起来,这个代理对象就取代了原有对象,任何对原对象的调用都首先经过代理,代理可以完成一些额外的任务,所以代理模式能够实现横切关注点。 可能在有些程序中有很多横切关注点,那么只需要在代理外再加几层代理即可。以银行账单为例介绍一个种用Java Reflection API动态代理实现的横切关注点模块化方法。系统提供了一个InvocationHandler接口: /系统提供的代理接口 public interface InvocationHandler public Object invoke(Object proxy, Method method, Object args) throw Throwable; 我们需要实现这个接口来创建一个日志代理,实现代码如下:/日志代理实现 public class LogHandler implement InvocationHandler private Object target; public LogHandler(Object target) this.target = target; public Object invoke(Object proxy, Method method, Object args ) throw Throwable /记录函数的初始状况参数等信息 (“开始:方法”+ method.getName() + “参数”+Arrays.toString(args) ); Object result = method.invoke(target, args); /记录函数的执行状况与返回值 (“结束:方法”+ method.getName() + “返回值”+ result ); 这样既可以使得日志操作不再零散分布于各个模块,易于管理。调用者可以通过如下方式调用: /主函数 public class Main public static void main(String args) ReportGenerator reportGeneratorImpl = new SMSReportGenerator (); /通过系统提供的Proxy.newProxyInstance创建动态代理实例 ReportGenerator reportGenerator = (ReportGenerator ) Proxy.newProxyInstance( reportGeneratorImpl.getClass().getClassLoader(), reportGeneratorImpl.getClass().getInterfaces(), new LogHandler(reportGeneratorImpl) ) ; . 代理模式很好的实现了横切关注点的模块化,解决了代码混乱代码分散问题,但是我们可以看出用 Java Reflection API 实现的动态代理结构十分复杂,不易理解,Spring框架利用了代理模式的思想,提出了一种基于JAVA注解(Annotation)和XML配置的面向切面编程方法(AOP ,Aspect-Oriented Programming)简化了编程过程。 C、Spring AOP 使用方法 Spring AOP使用中需要为横切关注点(有些时候也叫切面)实现一个类,银行账单的例子中,切面的实现如下:/切面模块实现 Aspect /注解1 public class LogAspect Before(“execution(* *.*(.)”) /注解2 public void LogBefore(JoinPoint joinPoint) throw Throwable (“开始:方法”+ joinPoint.getSignature().getName() ); After(“execution(* *.*(.)”) /注解3 public void LogA
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 智能制造系统开发与实施方案
- (2025年标准)电力移交协议书
- 2025广西柳州市第四十六中学教师招聘4人考试备考题库及答案解析
- 2025福建厦门海沧投资集团有限公司招聘1人考试模拟试题及答案解析
- 2025年杭州市上城区九堡街道社区卫生服务中心招聘编外4人笔试模拟试题及答案解析
- 2025年湖南常德石门县部分事业单位引进人才13人笔试模拟试题及答案解析
- (2025年标准)单位融资协议书
- 2025西安电子科技大学校医院招聘(2人)考试备考试题及答案解析
- 2025咸阳彬州市第三批见习岗位招聘(30人)笔试参考题库附答案解析
- 2025年聊城市三实人力资源服务有限公司招聘社会化工作者(5人)笔试模拟试题及答案解析
- 《上海地区公共数据分类分级指南》
- JTGT 3832-2018 公路工程预算定额 说明部分
- 煤矿培训课件:煤矿自救互救知识
- 新入职医生培训
- 2024年度软件即服务(SaaS)平台租赁合同3篇
- 2024年新课标培训2022年小学英语新课标学习培训课件
- 2024小学语文教学及说课课件:二年级上册《田家四季歌》
- 2024至2030年中国聚脲涂料行业市场发展调研及投资前景分析报告
- 1.1 鸦片战争 课件 2024-2025学年统编版八年级历史上册
- 2024至2030年中国演播室行业市场调查研究及发展战略规划报告
- DB11∕T 420-2019 电梯安装、改造、重大修理和维护保养自检规则
评论
0/150
提交评论