Spring之IOC

Spring3之IOC

Spring是一个轻量的控制反转和面向切面的容器框架。Spring框架所提供的众多功能之所以能成为一个整体,正是因为建立在IoC的基础之上。Spring是为了降低企业应用程序开发的复杂性而创建的,主要优势之一是分层架构,由7个定义良好的模块组成。这个7个模块如下:

 

组成spring框架的每个模块都可以单独存在,或者与其他一个或多个模块联合使用。整个Spring框架构建在Core核心模块之上,它是整个框架的基础。在该模块中,Spring为我们提供了了一个IoC容器实现,用于帮助我们以依赖注入方式管理对象之间的依赖关系。Spring的AOP框架采用Proxy模式构建,与IoC容器相结合,可以充分显示出Spring AOP的强大威力,Spring在Core核心模块和AOP模块的基础上,为我们提供了完备的数据访问和事务管理的抽象和集成服务。在数据访问支持方面,Spring对JDBCAPI的最佳实践极大地简化了该API的使用,除此之外,Spring框架为各种当前业界流行的ORM产品,比如Hibernate、ibatis、Toplink、JPA等提供了统一的集成支持。Spring框架中的事务管理抽象层是Spring AOP的最佳实践,它直接构建在SpringAOP上为我们提供了编程式事务管理和声明式事务管理的完备支持。Spring还有一套自己的Web MVC框架,职责分明的角色让这套框架看起来十分地“醒目”。spring的Porlet MVC构建在Spring Web MVC之上,延续了Spring Web MVC的一贯风格。整个模块一起就像是一棵树一样。

一、IoC介绍 

 IoC(Inversion Of Control)即控制反转,是由Martin Fowler在其一篇名为《Inversion of Control Container and the Dependency Injection pattern》的论文中提出的。IoC就是由容器来控制业务对象之间的依赖关系,而非传统方式中的由代码来直接操控。控制反转的本质,是控制权由应用代码中转到了外部容器,控制权的转移即是所谓的反转,其好处是降低了业务对象之间的依赖程度,即实现了解耦。

IoC的实现策略有两种,依赖查找:容器的受控对象通过容器的API来查找自己所依赖的资源和协作对象。这种方式虽然降低了对象间的依赖,但是同时也是用了容器的API,从而造成了我们无法在容器外使用和测试对象。依赖注入(Dependency Injection):对象只提供普通的方法给容器去让它决定依赖关系。容器全权负责组件的装配,它会把符合依赖关系的对象通过属性中的getter和setter,或是构造函数传递给需要的对象。我们将通过属性注入依赖关系的做法称为设值方法注入,将构造子参数传入的做法称为构造子注入。显然依赖注入的好处:查询依赖操作和应用代码分离,受控对象不会使用到容器的特定的API。 

二、IoC讲解

我们在开发一个应用系统时,需要开发大量的java类,系统将会通过这些java类之间的相互调用来产生作用。类与类之间的调用关系是系统类之间最直接的关系。我们可将其分为调用者和被调用者。类的调用方法有三种:自己创建、工厂模式、外部注入,可用new、get、set来说明这三个方式。new是自己创建,get是从别人那里取得,set是别人送进来。下面一个学生学习图书的例子,使用接口驱动编程方式,说明这个问题。

1)new-自己创建

                     Spring之IOC_第1张图片 

2)get-工厂模式

  Spring之IOC_第2张图片

3)set-外部注入

                 Spring之IOC_第3张图片 

显然第一种方式依赖于被调用者对象,第二种方式依赖于工厂,都存在依赖性。第三种方式完全抛开了依赖关系的枷锁,可以自由地由外部注入。这就是IoC,将对象的创建和获取提取到外部,并由外部容器提供需要的组件。 

在IoC模式中,为调用者设置被调用者对象包括三种类型:

type1--接口注入:服务需要实现专门的接口,通过接口,由对象提供这些服务。可以从对象查询依赖性,例如从JNDI或ServiceManager等获得被调用者。

type2--构造注入使依赖性以构造函数的形式提供,并不以JavaBean属性的形式公开。

type3--设置注入通过JavaBean的属性(例如setter方法)分配依赖性。

type1的接口注入模式具有侵入性,要求组件必须与特定的接口相关联,而type2和type3的依赖注入均具备无侵入性的特点。Spring 对type2和type3类型的依赖注入机制提供了良好的机制。

三种注入方式比较

接口注入:

接口注入模式因为具备侵入性,它要求组件必须与特定的接口相关联,因此并不被看好,实际使用有限。

Setter 注入:

对于习惯了传统 javabean 开发的程序员,通过 setter 方法设定依赖关系更加直观。

如果依赖关系较为复杂,那么构造子注入模式的构造函数也会相当庞大,而此时设值注入模式则更为简洁。

如果用到了第三方类库,可能要求我们的组件提供一个默认的构造函数,此时构造子注入模式也不适用。

构造器注入:

在构造期间完成一个完整的、合法的对象。

所有依赖关系在构造函数中集中呈现。

依赖关系在构造时由容器一次性设定,组件被创建之后一直处于相对“不变”的稳定状态。

只有组件的创建者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。

总结

Spring使用注入方式,为什么使用注入方式,这系列问题实际归结起来就是一句话,Spring的注入和IoC(本人关于IoC的阐述)反转控制是一回事。

理论上:第三种注入方式(构造函数注入)在符合java使用原则上更加合理,第二种注入方式(setter注入)作为补充。

实际上:我个人认为第二种注入方式(setter注入)可以取得更加直观的效果,在使用工作上有不可比拟的优势,所以setter注入依赖关系应用更加广泛。


三、IoC实现

在Spring中BeanFactory就是IoC容器的代表者,Spring容器中管理的对象就是Bean,这种Bean不同于javaBean,只是spring容器初始化、装配及管理的组件。 Spring IoC容器通过读取配置文件中的配置元数据,通过元数据对应用中的各个对象进行实例化及装配。一般使用基于xml配置文件进行配置元数据,而且Spring与配置文件是完全解耦的,可以使用其他任何可能的方式进行配置元数据,比如注解、基于java文件的、基于属性文件的配置都可以。

那Spring是如何实例化Bean,如何管理Bean以及Bean之间的依赖关系呢? 如下图:

                         

从上图看,Spring IoC容器首先会通过某种途径加载Configuration MetaData,然后进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了Bean定义必要信息的BeanDefinition中,注册到相应的BeanDefinitionRegistry,当某个请求方通过容器的getBean方法明确地请求某个对象,或者因依赖关系容器需要隐式地调用getBean方法时,容器查看被请求对象是否初始化,并进行注入依赖,当对象装配完毕之后,容器就会返回请求方使用。

 在Spring中,BeanFactory是IoC容器的核心接口,负责容纳XML文件所描述的Bean,并对Bean进行管理,包括实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。Spring为我们提供了许多易用的BeanFactory的实现,XmlBeanFactory就是最常用的一个。ApplicationContext接口扩展了BeanFactory,还提供了与Spring AOP集成、国际化处理、事件传播及提供不同层次的context实现 (如针对web应用的WebApplicationContext)。ApplicationContext 增加了更多支持企业级功能支持。其完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。使用Spring的BeanFactory,有三个步骤如下:

第一步:配置XML元数据;第二步:实例化容器;第三步:调用BeanFactory进行Bean操作。

其中实例化容器的方法有多种:

XmlBeanFactory:BeanFactory的实现,提供基本的IoC容器功能,可以从classpath或文件系统等获取资源;  

(1) File file = new File("beans.xml");

Resource resource = new FileSystemResource(file);

BeanFactory beanFactory = new XmlBeanFactory(resource);

(2) Resource resource = new ClassPathResource("beans.xml");

BeanFactory beanFactory = new XmlBeanFactory(resource);

ClassPathXmlApplicationContext:ApplicationContext的实现,从classpath获取配置文件;

BeanFactory beanFactory = new ClassPathXmlApplicationContext("beans.xml");

FileSystemXmlApplicationContext:ApplicationContext的实现,从文件系统获取配置文件。

BeanFactory beanFactory = new FileSystemXmlApplicationContext("beans.xml");

BeanFactory提供的方法及其简单,有6中方法供客户代码调用:

boolean  containsBean(String) :判断是否含有Bean;

  Object getBean(String):返回以给定名字注册的Bean实例;

Object getBean(String,Class):返回以给定名称注册的Bean实例,并转换为给定Class类型的实例;

Class getType(String name):返回给定名称的Bean的Class;

boolean isSingleton(String): 判断给定名称的Bean定义是否为Singleton模式,如果找不到则抛出异常;

String[] getAlias(String):返回给定Bean名称的所有别名。 

四、IoC配置

 1)配置bean元素,下面看bean的定义参数:

id属性,命名bean,是必选的;class属性,指定实例化的类,也是必选的。factory-method属性指定工厂方法,factory-bean属性指定工厂类,scope属性指定bean的作用域,depends-on属性指定依赖Bean,lazy-init属性指定是否延迟初始化bean,默认值是启用延迟初始化,init-method属性指定初始化回调方法,destroy-method属性指定析构回调方法,parent属性指定bean的父类。 

其中id必须是唯一的,不可重复,scope的设定有五种选择,Singleton、Prototype、Request、Session、Global Session,分别代表单例模式只允许有一个对象实例、每次请求重新创建一个对象实例、每次http请求创建对象实例、在一个Http Session中,一个Bean定义对应一个实例、在一个全局的HttpSession中,一个Bean定义对应一个实例,后三种作用域都是在基于Web的Spring ApplicationContext情形下有效。depends-on指定在初始化之前必须初始化的bean。

 2)依赖注入配置

在Spring中类的实例化有以下几种方式:

i.使用构造器实例化Bean

使用空构造器的情况下   <bean name=" " class=" " />   

构造器有参数的情况下  <bean  id=""  class="" >

<constructor-arg index="0" value="" /> 

</bean>

其中value表示常量值,也可以指定引用,指定引用使用ref来引用另一个Bean定义。如果id和name同时指定的话,则name表示别名,id是标识符。还可以通过类型,名称指定参数。

ii.使用静态工厂方法实例化Bean

该种配置方法就是指定factory-method方法 ,如下:

 <bean  id=""  class="" factory-method="">

<constructor-arg index="0" value="" /> 

</bean>

iii.使用实例工厂方法实例化Bean

该种配置下由于不可直接使用类调用方法,因此需要指定工厂类,如下:

 

  <bean  id=""  class="" factory-bean="" factory-method="" >

<constructor-arg index="0" value="" /> 

</bean>

  在Spring中属性的注入,需要配置的bean中有setter方法,在Spring中可以注入的属性可以是常量,引用,还可以是集合类等。

 

常量配置: <property name="constVar"  value="constVariable" />

引用配置: <property  name="refVar"   ref="refVariable" />  ,还可以使用ref子标签,idref标签在启动文件时若条件不满足便会抛出异常。引用配置的标签还有<ref local="" />,<ref parent="" />,<ref bean="" />等。

  空属性配置:<property name="password" > <null/>  </property>

List集合配置: <property name=" " >

<list >

<value>xxx</value>

</list> 

</property> 

  Set集合采用相似的配置。

Map集合配置: <property name="" >

<map>

<entry key=""  value="" />

</map>

</property>

Property配置:  <property  name="" >

<props>

<prop key="" >yyy</prop>

</props>

</property> 

  3)依赖属性检查

  在大规模的应用中,IoC容器中可能声明了几百个或者几千个Bean,这些Bean之间的依赖性往往非常复杂,设置方法注入的不足之一是无法确定一个属性将会被注入。检查所有必要的属性是否已经设置是非常困难的。Spring的依赖检查功能能够帮助检查一个Bean上的所有特定类型属性是否都已经设置。只需在bean的dependency-check属性中指定依赖检查模式就可以了。下面是Spring支持的所有依赖检查模式:

none:不执行依赖检查,任何属性都可以保持未设置状态。

simple:如果任何简单类型(原始和集合类型)的属性未设置,将抛出UnsatisfiedDependencyException异常。

object:如果任何对象类型属性没有设置,将抛出UnsatisfiedDependencyException异常。

all:如果任何类型的属性未设置,将抛出UnsatisfiedDependencyException异常。

默认模式为none,但是可以设置<beans>根元素的default-dependency-check属性来改变,这样将改变IoC容器中的所有Bean的默认依赖检查模式。

如果要对特定的属性进行检查,可使用@Required注解,Spring bean后处理器RequiredAnnotationBeanPostProcessor会检查带有@Required注解的所有Bean属性是否设置。 

  4)自动装配

  Spring中Bean和Bean之间互相访问时,我们需要显示指定引用装配它,使用<ref>设置,然后一个大的项目spring配置文件会十分庞大,手动装配比较麻烦,我们可以使用Spring提供的自动装配功能。自动装配通过配置<bean>标签的“autowire”属性来改变自动装配方式。Spring支持的自动装配模式:

no: 不执行自动装配,必须显示地装配依赖。

byName:对于每个Bean属性,装配一个同名的Bean。

byType:对于每个Bean属性,装配类型与之兼容的一个Bean。如果找到超过一个Bean,将抛出UnsatisfiedDependencyException异常。

Constructor: 对于每个构造程序参数,首先寻找与参数兼容的Bean。然后,选择具有最多匹配参数的构造程序。对于存在歧义的情况,将抛出UnsatisfiedDependencyException异常。

autodetect: 如果找到一个没有参数的默认构造程序,依赖将按照类型自动装配。否则,将由构造程序自动装配。 

默认模式是no,但是可以设置<beans>根元素的default-autowire属性修改,但是在实践中,我们建议仅将自动装配应用到组件依赖不复杂的应用程序中。

  还可以使用注解方式自动装配Bean,@Autowired和@Resource,需要注册一个AutowiredAnnotationBeanPostProcessor实例,也可以简单地包含<context:annotation-config />元素,这将自动注册一个AutowiredAnnotationBeanPostProcessor实例。@Autowired方式是按类型装配,而@Reource方式是默认按名称装配,当找不到和名称匹配的bean才会按类型装配。@Qualifier(xxx)为autowired方式提供一个按名称装配的候选Bean。 

5)加载资源和组件扫描

Spring能够从Classpath中自动检测组件,默认情况下,它能用特定的典型化注解检测所有组件。@Conponent指示Spring管理的基本组件 ,@Repository注解指示持久层的一个DAO组件,@Service注解指示服务层的一个业务组件,@Controller指示表现层的一个控制器组件。有了注解之后,我们需要声明一个xml元素<context:component-scan base-package=" " />要求Spring扫描这些注解。

在Spring中如果我们需要获取IoC容器的内部资源,Spring提供了提供了几种感知接口:

BeanNameAware:IoC容器中配置的实例的Bean名称。

BeanFactoryAware:当前的Bean工厂,通过它可以调用容器的服务。

ApplicationContextAware:当前应用上下文,通过它可以调用容器的服务。 

MessageSourceAware:消息资源,通过它可以解析文本消息。

ApplicationEventPublisherAware: 应用事件发布者,通过它可以发布应用事件。

ResourceLoaderAware: 资源装载器,通过它可以加载外部资源。

 感知接口中的设置方法在Bean属性设置之后、初始化回调方法调用之前调用,说明如下:

a)构造程序或者工厂方法创建bean实例

b)为Bean属性设置值和Bean引用。

c)调用感知接口中定义的设置方法。

d)调用初始化回调方法。

e) bean可以使用。

f) 容器关闭时,调用析构回调方法。 

有时候我们需要从不同位置(例如文件系统、classpath、URL)读取外部资源(如文本文件、XML文件、属性文件或图像文件)。在Spring中资源装载器提供统一的getResource()方法,按照资源路径读取外部资源。从文件系统加载转使用file前缀,如file:c:/shop/banner.txt。从classpath加载资源则使用classpath前缀,如classpath:com/spring/example/banner.txt。从URL加载如 http://xyz/banner.txt。

如果我们想把配置在外部文件的参数配置到bean属性中,如配置数据库时的参数。Spring有一个名为PropertyPlaceholderConfigure的Bean工厂后处理器,用来将部分Bean配置外部化为一个属性文件。在Bean配置文件中使用 ${var} 形式的变量,PropertyPlaceholderConfigure将从属性文件中加载属性并且用它们替代变量。如

 <bean id="propertyConfigurer"  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

     <property name="location">
        <value>/WEB-INF/jdbc.properties</value>
     </property>
</bean>

在使用时如下:  <property name="driverClassName">

          <value>${jdbc.driver}</value>
     </property>

五、IoC使用

  如上面的使用方法,下面提供简单的使用方法:

配置文件: <bean id="personService" class="org.spring.example.PersonService">

<property name="name" value="wawa" />
</bean>  

 

使用获取Bean:public class SpringTest {

public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext( "ApplicationContext.xml");
PersonService ps = (PersonService) ctx.getBean("personService");
ps.info();
}
}  


你可能感兴趣的:(spring)