《Spring揭秘》——IOC梳理1

依赖注入的三种方式:

1、构造方法注入:对象构造完成后,即进入就绪状态,可以马上使用。缺点是有时候构造方法的参数列表较长,构造方法无法被继承,无法设置默认值。

2、setter方法注入:相对宽松,可在对象构造完成后再注入。setter方法可以被继承,允许设置默认值。缺点是无法在构造完成后就进入就绪状态。

3、接口注入:类需要实现某个接口,接口定义了注入方法,方法的参数即为所依赖对象的类型。(不推荐,较为死板和烦琐。)

IoC Service Provider的职责:

业务对象的构建管理:业务对象无需关心所依赖的对象如何构建如何取得,交由IoC Service Provider进行构建;

业务对象的依赖绑定:IoC Service Provider的最终使命所在,通过结合之前构建和管理的所有业务对象,以及各个业务对象间可以识别的依赖关系,将这些对象所依赖的对象进行绑定。

Spring提供了两种容器类型:

BeanFactory:基础类型IOC容器,提供完整的IOC服务支持。默认采用延迟初始化策略,容器启动速度快,所要的资源有限。

ApplicationContext:ApplicationContext在BeanFactory的基础上构建,是相对高级的容器实现,拥有BeanFactory的所有支持,且提供了其他高级特性。其管理的对象会在该容器启动后,默认全部初始化并绑定完成。所以,该容器会需要更多系统资源。

BeanFactory的XML之旅:

<beans>:XML配置文件中最顶层的元素,它下面可以包含0个或1个<description>和多个<bean>以及<import>或者<alias>。属性设置包括:default-lazy-init 、default-autowire 、default-init-method 、default-destroy-method 、default-dependency-check 。

<description>:指定一些描述性信息。

<import>:如果A.xml中的<bean>定义可能依赖B.xml中的某些<bean>定义,那就在A.xml中使用<import>将B.xml引入到A.xml。

<alias>:当bean名称过长时,可以通过alias来指定别名。<alias name="aabbcc" alias="abc"/>

<bean>:id属性(内部bean可省)、class属性、(name属性)

<bean id="helloSpring" name="//test t" class="com.test.HelloSpring" ></bean>

可以通过ApplicationContext的getBean("helloSpring")获取,还可以用"//test","t"代替,name可通过逗号、空格分割开。

构造方法注入:<constructor-arg>,若存在多个构造方法,可能要使用type和index属性。要注意index从0开始。

<bean id="car1" class="com.test.Car">
    <constructor-arg value="Audi" index="0"></constructor-arg>
    <constructor-arg value="ShangHai" index="1"></constructor-arg>
    <constructor-arg value="300000.0" type="double"></constructor-arg>
</bean>

setter方法注入:要确保提供了无参构造方法。此外,<constructor-arg>和<property>可同时使用。

  <bean id="person" class="com.test.Person">
      <property name="name" value="Tom"/>
      <property name="age" value="24"/>
      <property name="car" ref="car2"/>
      <!--
      <property name="car">
          <ref bean="car2"/>     可以引用同一文件或父容器
          <ref local="car2"/>   只能引用同一配置文件的Bean
          <ref parent="car2"/>  引用父容器的Bean
      </property>
      -->
  </bean>

<list>:对应注入对象类型是java.util.List及其子类 或者 数组类型的依赖对象。有序。

<list>
    <ref bean="car1"></ref>
    <ref bean="car2"></ref>
    <bean class="com.test.Car"></bean>
</list>

<set>:对应注入对象类型是java.util.Set及其子类的依赖对象。无序。

<map>:对应注入对象类型是java.util.Map及其子类的依赖对象。

<map>
  <entry key="AA" value-ref="car1"></entry>
  <entry key="BB" value-ref="car2"></entry>
</map>

<props>:相当于简化后的<map>,key只为String类型。对应配置类型为java.util.Properties的对象依赖。

<bean id="dataSource" class="com.test.DataSource">
  <property name="properties">
    <props>
       <prop key="user">root</prop>
       <prop key="password">1234</prop>
    </props>
  </property>
</bean>

若想配置的集合可以被复用,则可使用<util:list>、<util:set>、<util:map>。

<util:set id ="strs">
  <value>str1</value>
  <value>str2</value>
  <value>str3</value>
</util:set>

c命名空间(构造器)和p命名空间(属性赋值):相对而言更加简洁。不能直接使用集合类。

<bean id="employee" class="com.test.Employee" p:name="Aaa" p:carList-ref="cars1"></bean>
<bean id="carLast" class="com.test.Car" c:brand="BRAND" c:corp="CORP" c:price="1" p:maxSpeed="111"></bean>

depend-on:使用depend-on来要求容器在初始化自身实例之前首先实例化其他的对象,要保证该对象一定先实例化。

autowire:5种自动绑定方式:no(默认),byName,byType,constructor,autodetect。推荐手动绑定,此外beans中也包含default-autowire,可进行统一配置。

dependency-check:对其所依赖的对象进行最终检查。不常使用。

lazy-init:延迟加载。注意的是若a对象设置了延迟加载,而其是另一个对象b的构造函数所需要的对象,b没有设置延迟加载,则a依然会被初始化。在beans属性中同样有default-lazy-init属性可进行统一配置。

bean的scope:singleton(默认,生命周期可直接参照IoC容器),prototype(当对象实例给请求方后,请求方负责对象后记生命周期的管理工作),request,session,global session。此外,自定义session参照P55。

关于继承:

<bean id="newsProviderTemplate" abstract="true">
  <property name="newPersistener">
    <ref bean="djNewsPersister"/>
  </property>
</bean>
<bean id="superNewsProvider" parent="newsProviderTemplate" class="..FXNewsProvider">
  <property name="newsListener">    <ref bean="djNewsListener">
  </property>
</bean>
<bean id="subNewsProvider" parent="newsProviderTemplate" class="..SpecificFXNewsProvider">
  <property name="newsListener">
    <ref bean="specificNewsListener"/>
  </property>
</bean>

newsProviderTemplate就相当于一个配置模板,这个bean不可以实例化,同时可以不指定class属性。当多个bean定义拥有多个相同属性配置的时候,会带来很大便利。此外,parent所指向的对象也可以不是abstract的,而是具体的。额外强调的一点:若不想容器在初始化的时候实例化某些对象,就可以将abstract属性设置为true,以避免容器实例化

工厂方法与 FactoryBean

1.静态工厂方法:

public class StaticBarInterfaceFactory {
    public static BarInterface getInstance() {
            return new BarInterfaceImpl();
    }
}

<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>                

容器调用该静态方法工厂类的指定工厂方法(getInstance),并返回方法调用后的结果,即BarInterfaceImpl的实例。

有的工厂类的工厂方法可能需要参数来返回相应实例, 可以通过<constructor-arg>来指定工厂方法需要的参数 :

public class StaticBarInterfaceFactory{
  public static BarInterface getInstance(Foobar foobar){
    return new BarInterfaceImpl(foobar);
  }
}

<bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance">
  <constructor-arg>
    <ref bean="foobar"/>
  </constructor-arg>
</bean>
<bean id="foobar" class="...FooBar"/>

针对静态工厂方法实现类的bean定义,使用<constructor-arg>传入的是工厂方法的参数,而不是静态工厂方法实现类的构造方法的参数。

2.非静态工厂方法:

public class NonStaticBarInterfaceFactory{
  public BarInterface getInstance(){
    return new BarInterfaceImpl();
  }
}<bean id="barFactory" class="...NonStaticBarInterfaceFactory"/><bean id="bar" factory-bean="barFactory" factory-method="getInstance"/> 

使用factory-bean属性来指定工厂方法所在的工厂类实例,而不是通过class属性来指定工厂方法所在类的类型。指定工厂方法名则相同,都是通过factory-method属性进行的。

如果非静态工厂方法调用时也需要提供参数的话,处理方式是与静态的工厂方法相似的,都可以通过<constructor-arg>来指定方法调用参数。

3.FactoryBean

FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,FactoryBean本身就是生产对象的工厂。

使用FactoryBean的情况:当某些对象的实例化过程过于烦琐,通过XML配置过于复杂,使我们宁愿使用Java代码来完成这个实例化过程的时候,或者,某些第三方库不能直接注册到Spring容器的时候, 就可以实现org.springframework.beans.factory.FactoryBean接口,给出自己的对象实例化逻辑代码。

public class CarFactoryBean implements FactoryBean<Car>{

    private String brand;
    public void setBrand(String brand) {
        this.brand = brand;
    }

    @Override
    public Car getObject() throws Exception {
        // TODO Auto-generated method stub
        return new Car(brand, 500000);
    }

    @Override
    public Class<?> getObjectType() {
        // TODO Auto-generated method stub
        return Car.class;
    }

    @Override
    public boolean isSingleton() {
        // TODO Auto-generated method stub
        return true;
    }
}

  <!-- 通过FactoryBean来配置实例  实际返回的实例通过getObject() -->
  <bean id="car" class="com.test.CarFactoryBean">
    <property name="brand" value="BMW"></property>
  </bean>

Spring容器内部许多地方了使用FactoryBean。比较常见的FactoryBean实现:JndiObjectFactoryBean;LocalSessionFactoryBean;SqlMapClientFactoryBean;ProxyFactoryBean;TransactionProxyFactoryBean。

方法注入与方法替换

<bean id="newsBean" class="..domain.FXNewsBean" scope="prototype"></bean>
<bean id="mockPersister" class="..impl.MockNewsPersister">
    <property name="newsBean">
       <ref bean="newsBean"/>
    </property>
</bean>

上文代码中,newsBean为prototype,mockPersister为singleton,存在setNewsBean方法。当调用persistNews方法时会打印newsBean的信息(调用getNewsBean())。当我们按照该代码配置并多次调用persistNews时会发现:每次调用的newsBean是同一个,虽然newsBean的scope是prototype。

原因:但当容器将一个FXNewsBean的实例注入MockNewsPersister之后, MockNewsPersister就会一直持有这个FXNewsBean实例的引用。虽然每次输出都调用了getNewsBean()方法并返回了 FXNewsBean 的实例,但实际上每次返回的都是MockNewsPersister持有的容器第一次注入的实例。这就是问题之所在,在第一个实例注入后, MockNewsPersister再也没有重新向容器申请新的实例。

方法注入:

容器会为我们要进行方法注入的对象使用Cglib动态生成一个子类实现,从而替代当前对象。(依赖cglib)

<bean id="newsBean" class="..domain.FXNewsBean" scope="prototype"></bean>
<bean id="mockPersister" class="..impl.MockNewsPersister">
  <lookup-method name="getNewsBean" bean="newsBean"/>
</bean>

通过<lookup-method>的name属性指定需要注入的方法名, bean属性指定需要注入的对象。使用lookup方法注入是有一定范围的,一般是在通过一个Singleton Bean获取一个Prototype Bean时使用。

补充方法:使用BeanFactoryAware接口
Spring框架提供了一个BeanFactoryAware接口,容器在实例化实现了该接口的bean定义的过程中,会自动将容器本身注入该bean。这样,该bean就持有了它所处的BeanFactory的引用。(其实将应用与Spring框架类绑定在一起,实为下策。)

public interface BeanFactoryAware {
  void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
public class MockNewsPersister implements IFXNewsPersister,BeanFactoryAware {
  private BeanFactory beanFactory;
  public void setBeanFactory(BeanFactory bf) throws BeansException {
    this.beanFactory = bf;
  }
  public void persistNews(FXNewsBean bean) {
    persistNews();
  }
  public void persistNews(){
    System.out.println("persist bean:"+getNewsBean());
  }
  public FXNewsBean getNewsBean() {
    return beanFactory.getBean("newsBean");  }}

<bean id="newsBean" class="..domain.FXNewsBean" scope="prototype"></bean><bean id="mockPersister" class="..impl.MockNewsPersister"></bean>

补充方法2:使用ObjectFactoryCreatingFactoryBean (P62)

方法替换:

方法替换主要体现在方法的实现层面上,它可以灵活替换或者以新的方法实现覆盖掉原来某个方法的实现逻辑。基本上可以认为,方法替换可以帮助我们实现简单的方法拦截功能。 (但其实使用不多,一般多使用AOP。)

<bean id="djNewsProvider" class="..FXNewsProvider">
  ...
  <replaced-method name="getAndPersistNews" replacer="providerReplacer"></replaced-method>
</bean>
<bean id="providerReplacer" class="..FXNewsProviderMethodReplacer"></bean>

注意providerReplacer 要implements MethodReplacer。(Spring3.x P108)

时间: 2024-08-24 05:08:30

《Spring揭秘》——IOC梳理1的相关文章

《Spring揭秘》(八)---- IoC容器及Bean的生命周期

Spring的IoC容器会以某种方式加载配置信息,然后根据这些信息绑定整个系统的对象,最终组装成一个可用的基于轻量级容器的应用系统.实现以上功能,分为两个阶段:容器启动阶段和Bean实例化阶段.而且Spring的IoC容器在每个阶段都加入了相应的扩展点,以便根据具体场景的需要加入自定义的扩展逻辑. 1 容器启动阶段 首先会通过某种途径加载配置信息,大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的配置信息进行解析和分析,并将分析后的信息编组为相应的BeanD

spring揭秘 读书笔记 二 BeanFactory的对象注冊与依赖绑定

本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,并且IoC Service Provider提供两个功能对象的创建,依赖关系的管理. 只是,IoC容器这个词中,我们还得关注容器二字.它还包括了一些别的功能,例如以下图 Spring提供了两种类型的容器,各自是BeanFactory与ApplicationContext. 它们的差别在于: BeanFactory:对于它所管理的bean,採

spring揭秘 读书笔记 一

本文是王福强所著<<spring揭秘>>一书的读书笔记 ioc的基本概念 一个例子 我们看下面这个类,getAndPersistNews方法干了四件事 1 通过newsListener获得所有的新闻id; 2 通过newsListener,用新闻id获得新闻实体 3 用newPersistener存储新闻实体 4 再使用newsListener发布新闻 public class FXNewsProvider { private IFXNewsListener newsListene

好莱坞原则—Spring的IOC容器

IOC容器的概念,之前在学习SSH的时候,就有接触过.但那时候也仅仅是知道这么个概念,认为它非常难理解.事实上并非它难理解,而是我并没有停下来好好对它总结梳理过. IOC(Inversion of Control)简单介绍: 控制反转",并非一种技术.而是一种思想.一种主动提供服务的思想.所谓IOC,就是由Spring负责控制对象的生命周期和对象间的关系,与我们传统的在对象内部直接控制背道而驰. 在传统的程序开发中,完毕一个业务逻辑至少须要两个或两个以上的对象协助完毕.通常一个对象要使用另外一个

spring揭秘 读书笔记 二

本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,而且IoC Service Provider提供两个功能对象的创建,依赖关系的管理. 不过,IoC容器这个词中,我们还得关注容器二字.它还包含了一些别的功能,如下图 Spring提供了两种类型的容器,分别是BeanFactory与ApplicationContext. 它们的区别在于: BeanFactory:对于它所管理的bean,采取的

spring中IOC的简单使用

spring的ioc主要就是依赖注入,有基于构造器的依赖注入还有通过设值注入,这里我只简单的实现设值注入的方法,通过spring的依赖管理,我们可以很方便的了解各层之间的依赖关系,降低了各层之间的耦合,我们可以不用过多的去关注对象的管理和创建,我们只需要去bean工厂申请即可,这样我们更多的注意力就可以放到真正的代码实现,而不用去关心代码的创建和销毁.,接下来时简单的设值注入. 我们先简单的去创建一个学生类和老师类 public class Student { public void outhi

Spring之IOC容器注入

上一篇做了IOC和AOP的原理介绍,这一篇主要讲解IOC的注入.不过我依然困惑一个问题: 一 : 依赖注入(DI)中有三种注入方式,那Spring中到底实现了几种方式?也是三种? IOC在很多框架中都有实现,并不是Spring特有的,之前说过IOC主要包含DL(Dependency Lookup)和DI(Dependency Injection),也就是说实现IOC的技术有很多,但是主要包含DI和DL,但是相对而言,DI应用范围比较广泛,我想这也是为什么Martin Fowler将控制反转用依赖

Spring的Ioc

引用:http://www.cnblogs.com/xdp-gacl/p/4249939.html 学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解. 一.分享Iteye的开涛对Ioc的精彩讲解 首先要分享的是Iteye的开涛这位技术牛人对Spring框

一点一点学架构(三)——Spring.NET IOC

IOC背景介绍 传统的实现: 由程序内部代码来控制类与类之间的关系(如:在一个具体的类中,调用另一个类的方法). 使用new关键字来实现两个类之间关系的组合. 这种实现方式会造成类之间耦合. IOC的实现: 它将类间关系从程序内部提到外部容器,也就是说由容器在运行期将类间的某种依赖关系动态注入类中. 对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A. IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的

聊聊spring的ioc

平时我们老说的控制反转依赖注入是什么东西?其实说白了就是一个松紧耦合的问题,咱们可以想一想,一个javabean里面可能会有很多属性比如像下面的代码: public class KnightOfTheRoundTable implements Knight { private String name; private Quest quest; public KnightOfTheRoundTable(String name) { this.name = name; } public Object