Java后端精选技术:Spring框架简介( 二 )
SMSReportGenerator()大量使用的话 , 修改就会十分复杂 , 一个声明没有修改就会出现大的BUG 。
所以需要一种划分 , 让各个模块尽可能独立 , 当开发人员B修改自己的模块时 , 开发人员A不需要修改任何代码 。
B、问题出现的原因
为例子中的程序画一个UML依赖图:

文章插图
可以发现上述问题出现的原因主要是:模块A与模块B不但存在接口依赖 , 还存在实现依赖 。ReportGenerator每次修改它的实现 , 都会对ReportService产生影响 。那么需要重构消除这种实现依赖 。
C、用容器解决问题
消除实现依赖一般可以通过添加一个容器类来解决 。在例子程序容器代码如下:

文章插图
使用容器后 , 模块A的ReportService的属性实现方法也发生了变化 。

文章插图
这样的话 , 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的写法如下:

文章插图
在容器中把依赖注入:

文章插图
这样一来ReportService就不用管SMSReportGenertor在容器中是什么名字 , 模块A对于模块B只有接口依赖 , 做到了松耦合 。
C、Spring IoC容器的XML配置
每个使用Spring框架的工程都会用到容器与控制反转 , 为了代码复用 , Spring把通用的代码独立出来形成了自己的IoC容器供开发者使用:

文章插图
与上面例子中实现的容器相比 , Spring框架提供的IoC容器要远远复杂的多 , 但用户不用关心Spring
IoC容器的代码实现 , Spring提供了一种简便的bean依赖关系配置方式------使用XML文件 , 在上面的例子中 , 配置依赖关系只要在工程根目录下的“Application.xml”编辑如下内容:
推荐阅读
- eclipse下创建Maven风格的java项目
- java中的参数传递
- Java 如何实现线程死锁?
- 如何搭建java的运行环境
- Java多线程编程——锁优化
- 九种跨域方式实现原理
- Java泛型大全
- JavaScript获取json中key所对应的value值的简单方法
- Java进程CPU占用高导致的网页请求超时的故障排查
- 谁说JAVA不适合做神经网络,那来看看Encog这款框架吧