Spring的本质是为了提供各种服务,帮助我们简化基于POJO(Plain Old Java Object,简单Java对象)的Java应用程序开发。Spring框架为POJO提供的各种服务共同组成了Spring的生命之树。
组成整个Spring框架的各种服务实现被划分到了多个相互独立却又相互依赖的模板当中。
整个Spring框架构建在Core核心模板之上,它是真个框架的基础。在该块模板上,Spring为我们提供了一个Ioc容器(Ioc Container)实现,用于帮助我们以一路来注入的方式管理对象之间的依赖关系。除此之外,Core核心模板中还包括框架内部使用的各种工具类(如果愿意,我们也可以在框架之外使用)。
沿着Spring生命之树往上左序遍历,我们将来到AOP模板,该模板提供了一个轻便但功能强大的AOP框架,让我们可以以AOP的形式增强各OPJO的能力,进而不足OOP/OOSD之缺憾。Spring的AOP框架符合AOP Alliance规范,采用Proxy模式构建,与Ioc容器相结合,可以充分显示出Spring AOP的强大威力。
继续上行,Spring框架在Core核心模板和AOP模板的基础上,为我们提供了完备的数据访问和事务管理的抽象和集成服务。在数据访问支持方面,Spring对JDBC API的最佳实践极大地简化了该API的使用。除此之外,Spring框架为各种当前业界流行的ORM产品,比如Hibernate、iBATIS、Toplink、JPA等提供了形式统一的集成支持。Spring框架中的事务管理抽象层是Spring AOP的最佳实践,它直接构建在Spring AOP的基础之上,为我们提供了编程式事务管理和声明式事务管理的完备支持。
为了简化各种Java EE服务(像JNDI、JMS以及JavaMail等)的使用,Spring框架为我们提供了针对这些Java EE服务的继承服务。在Spring的帮助下,这些Java EE服务现在都变成不再烦琐难用。使用Spring框架构建的基于POJO的应用程序如果也需要 远程访问或者公开一些服务的话,Spring的Remoting框架将帮助它完成这一使命。
最后提到的就是Web模块。在该模块中,Spring框架提供了一套自己的Web MVC框架,职责分明的角色划分让这套框架看起来十分地“醒目”。
Spring 的 Ioc容器:
Spring的Ioc容器是整个Spring框架的核心和基础。
IOC全称为Inversion of Control,中文通常翻译为“控制反转”,它还有一个别名叫做依赖注入(Dependency Injection)。
为了更好地阐述Ioc模式的概念,我们引入以下简单场景。
在FX(Foregin Exchange)项目中,经常需要近乎实时地为客户提供外汇新闻。通常情况下,都是先从不同的新闻社订阅新闻来源,然后通过批处理程序定时地到指定的新闻服务器抓取最新的外汇新闻,接着将这些新闻存入本地数据库,最后在FX系统的前台界面显示。
假设我们有一个FXNewsProvider类来做以上工作。
通常我们依赖于某个类和服务,最简单而有效的方式就是直接在类的构造函数中新建相应的依赖类。
可是回头想想,我们自己每次用到什么依赖对象都要主动去获取,这是否真的必要?我们最终要做的,其实就是直接调用依赖对象所提供的某项服务而已。只要用到这个依赖对象的时候,他能够准备就绪,我们完全可以不管这个对象是自己找来的还是别人送来的。
IOC就是为了帮助我们避免之前的“大费周折”,而提供了更加轻松简洁的方式。
通常情况下,被注入对象会直接依赖于被依赖对象。但是,在Ioc的场景中,二者之间通过IocService Provider来打交道,所有的被注入对象和依赖对象现在由IOc Service Provider统一管理。
Ioc有三种依赖注入的方式。即构造方法注入(constructor injection)、setter方法注入(setter injection)以及接口注入(interface injection)。
构造方法注入:
顾名思义,构造方法注入,就是被注入对象可以通过在其构造方法中声明依赖对象的参数列表,让外部(通常是ioc容器)知道它需要哪些依赖对象。
Ioc Service Provider会检查被注入对象的构造方法,取得它所需要的依赖对象列表,进而为其注入相应的对象。同一个对象是不可能被构造两次的。因此,被注入对象的构造乃至整个生命周期,应该是由IOc Service Provider来管理的。
构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
setter方法注入
对于JavaBean对象来说,通常会通过setXXX()和getXXX()方法来访问对应属性。这些setXXX()方法统称为setter方法,getXXX()就称为getter方法。通过setter方法,可以更改相应的对象属性,通过getter方法,可以获得相应属性的状态。所以,当前对象只要为其依赖对象所对应的属性添加setter方法,就看可以通过setter方法将响应的依赖对象设置到被注入对象中。
setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些,可以在对象构造完成后再注入。
接口注入:
相对于前两种方式来说,接口注入没有那么简单明了。被注入对象如果想要IOC Service Provider为其注入依赖对象,就必须实现某个接口,这个接口提供一个方法,用来为其注入依赖对象。Ioc Service Provider最终通过这些接口来了解应该为被注入对象注入什么依赖对象。
FXNewsProvider为了让Ioc Service Provider为其注入所依赖的IFXNewsListener,首先需要实现IFXNewsListenerCallable接口,这个接口会声明一个injectNewsListner方法(方法名随意),该方法的参数,就是所依赖对象的类型。这样,InjectionServiceContainer对象,即对应的Ioc Service Provider就可以通过这个接口方法将依赖对象注入到被注入对象FXNewsProvider当中。
三种注入方式的比较:
接口注入:从注入方式的使用来说,可口注入基本处于“退役状态”。因为它强制被注入对象实现不必要的接口,带有侵入性。
构造方法注入:这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用。缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且在java中,构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。
setter方法注入:因为方法可以命名,所以setter方法注入在描述性上要比构造方法注入好一些。另外setter方法可以被继承,允许设置默认值,而且有良好的ide支持。缺点当然就是对象无法在构造完成后马上进入就绪状态。
掌控大局的Ioc Service Provider