• 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-自己创建

                          

    2)get-工厂模式

     

    3)set-外部注入

                      

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

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

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

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

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

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

    三、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();
    }
    } 
  • 相关阅读:
    Java IO2
    Java IO1 复制粘贴文件
    Java superArray2
    17杭州女子专场赛
    组队赛 A Promotions 深搜+逆向思维
    Java处理异常小试
    再谈Dijkstra算法和堆优化
    仿照JAVA vector模型写一个SuperArray
    HDU 2017女生赛04 (变形最短路)
    Gym-100712J 桶排序思想&反向思维
  • 原文地址:https://www.cnblogs.com/kingcucumber/p/2841929.html
Copyright © 2020-2023  润新知