一、轻量级容器
1、何为容器?
容器:应用代码的运行框架,提供对业务对象的管理。重量级容器EJB,曾经是管理J2EE业务对象最常用的容器。因为Spring的出现,它辉煌不再。J2EE的Web容器是比较特殊的,它用于管理servlet及其相关依赖对象。容器作为程序的运行框架,应该提供生命周期管理,查找服务,配置管理和依赖决策等基础服务,当然还可以提供企业级服务(事务管理等),线程管理,对象池,集群服务,远程服务等增值服务。
2、为何需要容器?
简单地说,使用容器就是为了减少代码量,特别是重复代码量。当然,容器提供的以下特性也是非常重要的。
可接插性:容器可以为各种组件提供可插接性。以Spring来说,通过配置文件XML或注解实现不同业务对象之间的切换,比如数据源等,避免硬编码带来的低灵活性和扩展性。
一致性:容器为用户提供了一致的保证,比如配置管理,服务定位等。Spring中JdbcTemplate,HibernateTemplate等抽象层为用户操作数据库,O/R映射提供一致的操作且简化不必要的,重复的代码。
一条龙服务:找到容器就能使用容器提供的所有服务,不需要为每个对象专门设计单例或工厂,避免Singleton泥潭及工厂类泛滥。
企业级服务:Spring提供的企业级服务足以说明使用容器的必要。
3、轻量级 VS 重量级
轻量级容器代表是Spring,重量级容器代表为EJB。EJB出现的较早,是Spring的前辈,更确切地说,Spring是为了解决EJB开发中的问题而产生的。EJB提供一个一步到位的解决方案,从对业务对象的管理,到为受控对象提供声明式中间件服务。正因为提供这样的解决方案,导致用户单独使用EJB管理业务对象(细粒度)或提供声明式服务成为噩梦。同时由于EJB实现问题,导致EJB代码必须依赖启动缓慢的EJB容器,从而影响测试。EJB部署也是个问题。
与EJB这个重量级容器相比,Spring作为轻量级容器的翘楚具有以下优点:
脱离整体式容器:轻量级容器和商业web服务器组合,不仅可以提供关键的J2EE服务,也能摆脱EJB式的整体式容器。
提高代码复用度:Spring中业务对象不依赖容器APi,可以高度复用。
更好地面向对象:Spring容器中的业务对象可以是POJO,且不用依赖容器API。
提高生产效率:轻量级容器中,应用代码类似于普通的Java代码,不需要像EJB那样依赖容器,所以可以充分利用IDE,提供开发效率。
提高可测试性:轻量级容器允许在容器外完成测试,不需要做特殊设置。
在重量级与轻量级的对抗中,Spring以四两拨千斤的招式重伤了EJB,可谓是长江后浪推前浪,EJB不得不追随后辈而做出改变。
二、控制反转
Spring是如何实现轻量级容器的?答曰:控制反转和面向切面编程。控制反转简称IOC,是一个非常重要的概念,它最著名的形式就是所谓的好莱坞原则(在设计模式初探-模板方法模式中有介绍)。
1、IOC实现策略
依赖查找:容器提供回调接口和上下文环境给组件,组件使用容器提供的API查找资源和协作对象。控制反转仅体现在回调方法上:容器将调用这些回调方法,从而让应用代码获取相关资源。
依赖注入(DI):组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。容器全权负责组件的装配,它会把符合依赖关系的对象通过JavaBean属性或构造器参数传递给需要的对象。
注:此处组件指的是相互协作的一组对象。
2、两种实现策略的比较
依赖查找:EJB提供的了依赖查找形式的IoC,让容器管理对象的生命周期,受理对象负责查找自己的依赖关系。这种实现方式的缺点是业务对象需要自己查找依赖关系,而有些时候查找还必须依赖特定的环境,比如JNDI。实际上,业务对象应该只负责实现业务逻辑,而不应该处理JNDI的技术细节。
依赖注入:Spring采用了依赖注入形式的IoC,让容器全权负责依赖查询,受管对象只需暴露JavaBean的setter方法或带参数的构造器,使容器可以在初始化时组装对象的依赖关系。此方式不依赖于特定的容器API或接口。业务对象不需负责查找资源或其他协作组件,降低应用代码对容器的依赖。
注:看到这大家要清楚IoC不等于DI!
三、Spring的IoC容器
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <!--id用于指定 bean一个唯一的bean定义,class定义bean的类型,需使用全限定名--> <bean id="mystyle" class="study.spring.container.TextStyle"> <!--property对应bean中的一个属性 --> <property name="fontSize" value="12"/> <property name="fontColor" value="red"/> <property name="fontFamily" value="宋体"/> </bean> <bean id="yourstyle" class="study.spring.container.TextStyle"> <property name="fontSize" value="14"/> <property name="fontColor" value="green"/> <property name="fontFamily" value="仿宋"/> </bean> </beans>
ApplicationContext ctx = new ClassPathXmlApplicationContext("sample_01.xml");使用基于Xml且从classpath加载资源文件的应用上下文实现。它会解析sample_01.xml,在使用容器获取相应bean时,会自动装配bean及其依赖。
ApplicationContext ctx = new ClassPathXmlApplicationContext("sample_01.xml"); TextStyle my = ctx.getBean("mystyle",TextStyle.class); System.out.println(my.getFontColor()); TextStyle your = ctx.getBean("yourstyle",TextStyle.class); System.out.println(your.getFontColor());使用应用上下文可访问从指定配置文件加载的bean。一般不推荐直接使用getBean方法获取bean对象,这样会将应用代码和Spring的API绑定。可以使用Spring的依赖注入功能,比如集成Spring的web框架时可以使用依赖注入管理Controller。