Spring揭秘3:IOC容器之BeanFactory的XML配置管理bean

BeanFactory的XML配置管理bean

是XML配置文件中最顶层的元素,它下面可以包含0或者1个和多个以及或者

在这里插入图片描述

的配置:的通用配置

作为所有的“统帅”,它拥有相应的属性(attribute)对所辖的进行统一的默认行为设置,包括如下几个。

可以统一管理的属性,即在配置。

  • default-lazy-init。其值可以指定为true或者false,默认值为false。用来标志是否对所有的进行延迟初始化

  • default-autowire。可以取值为no、byName、byType、constructor以及autodetect。默认值为no,如果使用自动绑定的话,用来标志全体bean使用哪一种默认绑定方式

  • default-dependency-check。可以取值none、objects、simple以及all,默认值为none, 即不做依赖检查

  • default-init-method。如果所管辖的按照某种规则,都有同样名称的初始化方法的话,可以在这里统一指定这个初始化方法名,而不用在每一个上都重复单独指定。

  • default-destroy-method。与default-init-method相对应,如果所管辖的bean有按照某种规则使用了相同名称的对象销毁方法,可以通过这个属性统一指定。

的配置

lazy-init

延迟初始化(lazy-init)这个特性的作用,主要是可以针对ApplicationContext容器的bean 初始化行为施以更多控制。与BeanFactory不同,ApplicationContext在容器启动的时候,就会马 上对所有的“singleton的bean定义”①进行实例化操作。通常这种默认行为是好的,因为如果系统有问 题的话,可以在第一时间发现这些问题,但有时,我们不想某些bean定义在容器启动后就直接实例化, 可能出于容器启动时间的考虑,也可能出于其他原因的考虑。总之,我们想改变某个或者某些bean定 义在ApplicationContext容器中的默认实例化时机。这时,就可以通过的lazy-init属性来 控制这种初始化行为。

<bean id="lazy-init-bean" class="..." lazy-init="true"/> 
<bean id="not-lazy-init-bean" class="..."/> 

ApplicationContext容器在启动的时候,只会默认实例化not-lazy-init-bean而不会实例化lazy-init-bean。

当然,仅指定lazy-init-bean的lazy-init为true,并不意味着容器就一定会延迟初始化该bean 的实例。如果某个非延迟初始化的bean定义依赖于lazy-init-bean,那么毫无疑问,按照依赖决计 的顺序,容器还是会首先实例化lazy-init-bean,然后再实例化后者。

<beans>
    <bean id="lazy-init-bean" class="..." lazy-init="true"/>
    <bean id="not-lazy-init-bean" class="...">
        <property name="propName">
            <ref bean="lazy-init-bean"/>
        </property>
    </bean>
</beans>

autowire

根据bean定义的某些特点将相 互依赖的某些bean直接自动绑定的功能。通过的autowire属性,可以指定当前bean定义采用某 种类型的自动绑定模式。

Spring提供了5种自动绑定模式,即no、byName、byType、constructor和autodetect,下面是 它们的具体介绍。

  • no:容器默认的自动绑定模式,也就是不采用任何形式的自动绑定,完全依赖手工明确配置各个bean 之间的依赖关系。

    <bean id="beanName" class="..." autowire="no"/> 
    
  • byName:按照类中声明的实例变量的名称,与XML配置文件中声明的bean定义的beanName的值进行匹配, 相匹配的bean定义将被自动绑定到当前实例变量上。这种方式对类定义和配置的bean定义有一定的限制。

    public class Foo {
        private Bar emphasisAttribute; 
            ...
        // 相应的setter方法定义
    }
    
    public class Bar { 
            ...
    }
    

    使用下面代码自动绑定

    <bean id="fooBean" class="...Foo" autowire="byName"> 
    </bean> 
    <bean id="emphasisAttribute" class="...Bar"> 
    </bean> 
    
    

    需要注意两点:

    第一,我们并没有明确指定fooBean的依赖关系,而仅指定了它的autowire属性 为byName;

    第二,第二个bean定义的id为emphasisAttribute,与Foo类中的实例变量名称相同。

  • byType:容器会根据当前bean定义类型,分析其相应的依赖对象类型,然后到容器所管理的所有bean定义中寻找与依赖对象类型相同的bean定义,然后将找到的符合条件的bean自动绑定到当前bean定义。

  • constructor:针对 构造方法参数的类型而进行的自动绑定

  • autodetect:这种模式是byType和constructor模式的结合体,如果对象拥有默认无参数的构造方法,容器会 优先考虑byType的自动绑定模式。否则,会使用constructor模式。当然,如果通过构造方法注入绑 定后还有其他属性没有绑定,容器也会使用byType对剩余的对象属性进行自动绑定。

dependency-check

检查每个对象某种类型的所有依赖是否全部已经注入完成,不过可能无法细化 到具体的类型检查。但某些时候,使用setter方法注入就是为了拥有某种可以设置也可以不设置的灵活性,所以,这种依赖检查并非十分有用,尤其是在手动明确绑定依赖关系的情况下。

通过dependency-check指定容器 帮我们检查某种类型的依赖,基本上有如下4种类型的依赖检查。

  • none。不做依赖检查。默认情况下,容器以此为默认值。
  • simple。如果将dependency-check的值指定为simple,那么容器会对简单属性类型以及相 关的collection进行依赖检查,对象引用类型的依赖除外。
  • object。只对对象引用类型依赖进行检查。
  • all。将simple和object相结合,也就是说会对简单属性类型以及相应的collection和所有对 象引用类型的依赖进行检查。

继承:parent

<beans>
    <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>
</beans>

的配置:id、name、class、依赖注入管理

id、name、class

可以使用id来指定在容器中的标志,还可以使用name属性来指定的别名(alias)。比如,以上定义,我们还可以像如下代码这样,为其添加别名:

<bean id="djNewsListener" name="/news/djNewsListener,dowJonesNewsListener"  class="..impl.DowJonesNewsListener"> 
</bean> 

与id属性相比,name属性的灵活之处在于,name可以使用id不能使用的一些字符,比如/。而且 还可以通过逗号、空格或者冒号分割指定多个name。name的作用跟使用为id指定多个别名基 本相同:

<alias name="djNewsListener" alias="/news/djNewsListener"/> 
<alias name="djNewsListener" alias="dowJonesNewsListener"/> 

class属性

每个注册到容器的对象都需要通过元素的class属性指定其类型,否则,容器可不知道这
个对象到底是何方神圣。在大部分情况下,该属性是必须的。仅在少数情况下不需要指定,如后面将提到的在使用抽象配置模板的情况下。

构造方法注入:

<bean id="djNewsProvider" class="..FXNewsProvider"> 
    <constructor-arg ref="djNewsListener"/> 
    <constructor-arg ref="djNewsPersister"/> 
</bean> 

type属性:指定构造函数的参数类型

<bean id="mockBO" class="..MockBusinessObject"> 
    <constructor-arg type = "int"> 
        <value>1111</value>
    <constructor-arg/> 
</bean> 

index属性:多个相同类型的参数,想要按照顺序定好

当某个业务对象的构造方法同时传入了多个类型相同的参数时,Spring又该如何将这些配置中的 信息与实际对象的参数一一对应呢?好在,如果配置项信息和对象参数可以按照顺序初步对应的话,

<bean id="mockBO" class="..MockBusinessObject"> 
    <constructor-arg index="1" value="11111"/> 
    <constructor-arg index="0" value="22222"/> 
</bean> 

setter方法注入:

有一个name属性(attribute),用来指定该将会注入的对象所对应的实例变量名称。之后通过value或者ref属性或者内嵌的其他元素来指定具体的依赖对象引用或者值,如以 下代码所示:

<bean id="djNewsProvider" class="..FXNewsProvider">
    <property name="newsListener">
        <ref bean="djNewsListener"/>
    </property>
    <property name="newPersistener">
        <ref bean="djNewsPersister"/>
    </property>
</bean>

只是用依赖注入的话,确保提供了默认的构造方法。

<bean id="djNewsProvider" class="..FXNewsProvider">
    <property name="newsListener" ref="djNewsListener"/>
    <property name="newPersistener" ref="djNewsPersister"/>
</bean> 

setter方法注入和的构造方法注入结合

<bean id="mockBO" class="..MockBusinessObject">
    <constructor-arg value="11111"/>
    <property name="dependency2" value="22222"/>
</bean> 

和可用配置项:包括bean、ref、idref、value、null、list、set、map、 props。以下涉及的所有内嵌元素,对于 以下涉及的所有内嵌元素,对于和都是通用的。

  • 。可以通过value为主体对象注入简单的数据类型,不但可以指定String类型的数据, 而且可以指定其他Java语言中的原始类型以及它们的包装器(wrapper)类型,比如int、Integer等。 容器在注入的时候,会做适当的转换工作(我们会在后面揭示转换的奥秘)。

    <constructor-arg value="111111"/> 
    <property name="attributeName" value="222222"/>
    
  • 。使用ref来引用容器中其他的对象实例,可以通过ref的local、parent和bean属性来指定引用的对象的beanName是什么。

    <constructor-arg>
        <ref local="djNewsPersister"/>
    </constructor-arg>
    <constructor-arg>
        <ref parent="djNewsPersister"/>
    </constructor-arg>
    <constructor-arg>
        <ref bean="djNewsPersister"/>
    </constructor-arg>
    

    local、parent和bean的区别在于:

    local只能指定与当前配置的对象在同一个配置文件的对象定义的名称(可以获得XML解析器 的id约束验证支持);

    parent则只能指定位于当前容器的父容器中定义的对象引用

    BeanFactory可以分层次(通过实现HierarchicalBeanFactory接口),容器A在初始化的时候,可以首先加载容器B中的所有对象定义,然后再加载自身的对象定义,这样,容器 B就成为了容器A的父容器,容器A可以引用容器B中的所有对象定义:

            BeanFactory parentContainer = new XmlBeanFactory(new ClassPathResource("父容器配置文件路径"));
            BeanFactory childContainer = new XmlBeanFactory(new ClassPathResource("子容器配置文件路径"), parentContainer);
    

    childContainer中定义的对象,如果通过parent指定依赖,则只能引用parentContainer中 的对象定义。

    bean则基本上通吃,所以,通常情况下,直接使用bean来指定对象引用就可以了。

  • 。使用idref,容器在解析配置的时候就可以帮你检查这个beanName到底是否存在,而不用等到运行时才发现这个beanName对应的对象实例不存在。

    <property name="newsListenerBeanName">
        <idref bean="djNewsListener"/>
    </property>
    
  • 内部。使用可以引用容器中独立定义的对象定义。但有时,可能我们所依赖的对 象只有当前一个对象引用,或者某个对象定义我们不想其他对象通过引用到它。这时,我们可以使用内嵌的,将这个私有的对象定义仅局限在当前对象。

    <bean id="djNewsProvider" class="..FXNewsProvider">
        <constructor-arg index="0">
            <bean class="..impl.DowJonesNewsListener">
            </bean>
        </constructor-arg>
        <constructor-arg index="1">
            <ref bean="djNewsPersister"/>
        </constructor-arg>
    </bean>
    
  • 。对应注入对象类型为java.util.List及其子类或者数组类型的依赖对象。通过可以有序地为当前对象注入以collection形式声明的依赖。

    <property name="param2">
        <list>
            <value>stringValue1</value>
            <value>stringValue2</value>
        </list>
    </property>
    
  • 。可以无序地为当前对象注入依赖。对应注入Java Collection中类型为java.util. Set或者其子类的依赖对象。

    <property name="valueSet">
        <set>
            <value>something</value>
            <ref bean="someBeanName"/>
            <bean class="..."/>
            <list>
                ...
            </list>
        </set>
    </property>
    
  • 。与列表(list)使用数字下标来标识元素不同,映射(map)可以通过指定的键(key) 来获取相应的值。与和的相同点在于,都是为主体对象注入Collection类型的依赖,不同点在于它对应注入java.util.Map或者其子类类型的依赖对象。

    <property name="mapping">
        <map>
            <entry key="strValueKey">
                <value>something</value>
            </entry>
            <entry>
                <key>objectKey</key>
                <ref bean="someObject"/>
            </entry>
            <entry key-ref="lstKey">
                <list>
                    ...
                </list>
            </entry>
            ...
        </map>
    </property>
    

    对于来说,它可以内嵌任意多个,每一个都需要为其指定一个键和一个值, 就跟真正的java.util.Map所要求的一样。

    • 指定entry的键。可以使用的属性——key或者key-ref来指定键,也可以使用的内嵌元素来指定键。
    • 指定entry对应的值。内部可以使用的元素,除了是用来指定键的,其他元素可以任意使用,来指定entry对应的值。除了之前提到的那些元素,还包括马上就要谈到的 。如果对应的值只是简单的原始类型或者单一的对象引用,也可以直接使用的value或者value-ref这两个属性来指定。
    • key属性用于指定通常的简单类型的键,而key-ref则用于指定对象的引用作为键。

    简化版map

    <property name="valueSet">
        <map>
            <entry key="strValueKey" value="something"/>
            <entry key-ref="" value-ref="someObject"/>
            <entry key-ref="lstKey">
                <list>
                    ...
                </list>
            </entry>
            ...
        </map>
    </property>
    
  • 。是简化后了的,或者说是特殊化的map,该元素对应配置类型为 java.util.Properties的对象依赖。Properties只能指定String类型的键(key)和值

    <property name="valueSet">
        <props>
            <prop key="author">fujohnwang@gmail.com</prop>
            <prop key="support">support@spring21.cn</prop>
            ...
        </props>
    </property>
    

    每个可以嵌套多个,每个通过其key属性来指定键,在内部直接指 定其所对应的值。内部没有任何元素可以使用,只能指定字符串,这个是由java.util. Properties的语意决定的。

  • 。它只是一个空元素,为对应的值注入null的话,请使用。

    <property name="param2">
        <null/>
    </property>
    

depends-on

让容器在实例化对象A之前首先实例化对象B。

    public class SystemConfigurationSetup {
        static {
            DOMConfigurator.configure("配置文件路径");
            // 其他初始化代码
        } 
         ...
    }

ClassA需要使用configSetup。

<bean id="classAInstance" class="...ClassA" depends-on="configSetup,configSetup2..."/> 
<bean id="configSetup" class="SystemConfigurationSetup"/> 

、、:非必须,了解即可

可以通过在配置的文件中指定一些描述性的信息。通常情况下,该元素是省略的。

根据模块功能或者层次关系,将配置信息分门别类地放到多个配置文件中。在想加载主要配置文件,并将主要配置文件所依赖的配置文件同时加载时,可以在这个主要的配置文件中通过元素对其所依赖的配置文件进行引用。比如,如果A.xml中的定义可能依赖 B.xml中的某些定义,那么就可以在A.xml中使用将B.xml引入到A.xml,以类似于 的形式。

可以通过**为某些起一些“外号”(别名),通常情况下是为了减少输入。比如, 假设有个**,它的名称为dataSourceForMasterDatabase,你可以为其添加一个,像这样。以后通过 dataSourceForMasterDatabase或者masterDataSource来引用这个都可以,只要你觉得方便就行。

bean的scope:bean的生命周期管理

Spring容器最初提供了两种bean的scope类型:singleton和prototype,但发布2.0之后,又引入了另 外三种scope类型,即request、session和global session类型。

DTD: 
<bean id="mockObject1" class="...MockBusinessObject" singleton="false"/> 
XSD: 
<bean id="mockObject2" class="...MockBusinessObject" scope="prototype"/> 

1.singleton(默认):单例/共享

标记为拥有singleton scope的对象定义,在Spring的IoC容器中只存在一个实例,所有对该对象的引用将共享这个实例。该实例从容器启动,并因为第一次被请求而初始化之后,将一直存活到容器退出。

在这里插入图片描述

标记为singleton的bean是由容器来保证这种类型的bean在同一个容器中只存在一个共享实例;
而Singleton模式则是保证在同一个Classloader中只存在一个这种类型的实例。

特性:

  • 对象实例数量。singleton类型的bean定义,在一个容器中只存在一个共享实例,所有对该类型bean的依赖都引用这一单一实例。
  • 对象存活时间。singleton类型bean定义,从容器启动,到它第一次被请求而实例化开始,只要容器不销毁或者退出,该类型bean的单一实例就会一直存活。

2.prototype:每次都重新生成新的实例

容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。

虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继生命周期的管理工作,包括该对象的销毁。

也就是说,容器每次返回给请求方一个新的对象实例之后,就任由这个对象实例“自生自灭”了。

对于那些请求方不能共享使用的对象类型,应该将其bean定义的scope设置为prototype。这样,每个请求方可以得到自己对应的一个对象实例。

在这里插入图片描述

3.request、session和global session

在这里插入图片描述

只适用于Web应用程序,通常是与XmlWebApplicationContext共同使用,而这些将在第6部分详细讨论。不过,既然它们也属于scope的概念,这里就简单提几句。

  • request:Spring容器,即XmlWebApplicationContext会为每个HTTP请求创建一个全新的RequestProcessor对象供当前请求使用,当请求结束后,该对象实例的生命周期即告结束。当同时有10个HTTP请求进来的时候,容器会分别针对这10个请求返回10个全新的RequestProcessor对象实例,且它们之间互不干扰。从不是很严格的意义上说,request可以看作prototype的一种特例,除了场景更加具体之外,语意上差不多。
  • session:对于Web应用来说,放到session中的最普遍的信息就是用户的登录信息,对于这种放到session中的信息。Spring容器会为每个独立的session创建属于它们自己的全新的UserPreferences对象实例。与request相比,除了拥有session scope的bean的实例具有比request scope的bean可能更长的存活时间,其他方面真是没什么差别。
  • global session:global session只有应用在基于portlet的Web应用程序中才有意义,它映射到portlet的global范围的session。如果在普通的基于servlet的Web应用中使用了这个类型的scope,容器会将其作为普通的session类型的scope对待。

工厂方法与 FactoryBean

静态工厂方法

// 无参数
public class StaticBarInterfaceFactory {
    public static BarInterface getInstance() {
        return new BarInterfaceImpl();
    }
}

// 有参数
public class StaticBarInterfaceFactory {
    public static BarInterface getInstance(Foobar foobar) {
        return new BarInterfaceImpl(foobar);
    }
}
<beans>
    <!--无参数-->
    <bean id="foo" class="...Foo">
        <property name="barInterface">
            <ref bean="bar"/>
        </property>
    </bean>
    <bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance"/>

    <!--    有参数-->
    <bean id="foo" class="...Foo">
        <property name="barInterface">
            <ref bean="bar"/>
        </property>
    </bean>
    <bean id="bar" class="...StaticBarInterfaceFactory" factory-method="getInstance">
        <constructor-arg>
            <ref bean="foobar"/>
        </constructor-arg>
    </bean>
    <bean id="foobar" class="...FooBar"/>
</beans>

针对静态工厂方法实现类的bean定义,使用传入的是工厂方法的参数,而不是静态工厂方法实现类的构造方法的参数。(况且,静态工厂方法实现类也没有提供显式的构造方法。)

非静态工厂方法

工厂方法非静态的

public class NonStaticBarInterfaceFactory {
    public BarInterface getInstance() {
        return new BarInterfaceImpl();
    }
    ...
}
<beans>
    <bean id="foo" class="...Foo">
        <property name="barInterface">
            <ref bean="bar"/>
        </property>
    </bean>
    <bean id="barFactory" class="...NonStaticBarInterfaceFactory"/>
    <bean id="bar" factory-bean="barFactory" factory-method="getInstance"/>
</beans>

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

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

FactoryBean

FactoryBean是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口,请不要将其与容器名称BeanFactory相混淆。

FactoryBean,其主语是Bean,定语为Factory,也就是说,它本身与其他注册到容器的对象一样,只是一个Bean而已,只不过,这种类型的Bean本身就是生产对象的工厂(Factory)

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

当然,不使用FactoryBean,而像通常那样实现自定义的工厂方法类也是可以的。

实现并使用自己的 FactoryBean其实很简单, org.springframework.beans.factory. FactoryBean只定义了三个方法,如以下代码所示:

public interface FactoryBean {
    public class NonStaticBarInterfaceFactory {
        public BarInterface getInstance() {
            return new BarInterfaceImpl();
        }
    ...
    }

    Object getObject() throws Exception;

    Class getObjectType();

    boolean isSingleton();
}
  • getObject()方法会返回该FactoryBean“生产”的对象实例,我们需要实现该方法以给出自己的对象实例化逻辑;

  • getObjectType()方法仅返回getObject()方法所返回的对象的类型,如果预先无法确定,则返回null;

  • isSingleton()方法返回结果用于表明,工厂方法(getObject())所“生产”的对象是否要以singleton形式存在于容器中。如果以singleton形式存在,则返回true,否则返回false;

import org.joda.time.DateTime;import org.springframework.beans.factory.FactoryBean;

public class NextDayDateFactoryBean implements FactoryBean {
    public Object getObject() throws Exception {
        return new DateTime().plusDays(1);
    }

    public Class getObjectType() {
        return DateTime.class;
    }

    public boolean isSingleton() {
        return false;
    }
}

注册到容器

<beans>
    <bean id="nextDayDateDisplayer" class="...NextDayDateDisplayer">
        <property name="dateOfNextDay">
            <ref bean="nextDayDate"/>
        </property>
    </bean>
    <bean id="nextDayDate" class="...NextDayDateFactoryBean"></bean>
</beans>

NextDayDateDisplayer的定义

public class NextDayDateDisplayer {
    private DateTime dateOfNextDay;
    // 相应的setter方法    
    ...  
} 

类型定义的是datetime 而不是NextDayDateFactoryBean

通过正常的id引用,容器返回的是FactoryBean所“生产”的对象类型,而非FactoryBean实现本身。

方法注入和方法替换

方法注入

只要让getNewsBean方法声明符合规定的格式,并在配置文件中通知容器,当该方法被调用的时候,每次返回指定类型的对象实例即可。方法声明需要符合的规格定义如下:

<public|protected> [abstract] <return-type> theMethodName(no-arguments); 

该方法必须能够被子类实现或者覆写,因为容器会为我们要进行方法注入的对象使用Cglib动态生成一个子类实现,从而替代当前对象。既然我们的getNewsBean()方法已经满足以上方法声明格式,剩下唯一要做的就是配置该类

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

通过**的name属性指定需要注入的方法名**,bean属性指定需要注入的对象,当getNewsBean方法被调用的时候,容器可以每次返回一个新的FXNewsBean类型的实例。

使用BeanFactoryAware接口

Spring框架提供了一个BeanFactoryAware接口,容器在实例化实现了该接口的bean定义的过程中,会自动将容器本身注入该bean。这样,该bean就持有了它所处的BeanFactory的引用。BeanFactoryAware的定义如下代码所示:

public interface BeanFactoryAware {
    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

我们让MockNewsPersister实现该接口以持有其所处的BeanFactory的引用,这样MockNewsPersister的定义如代码清单4-36所示

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" singleton="false"></bean> <bean id="mockPersister" class="..impl.MockNewsPersister"></bean>
使用ObjectFactoryCreatingFactoryBean

ObjectFactoryCreatingFactoryBean是Spring提供的一个FactoryBean实现,它返回一个ObjectFactory实例。从ObjectFactoryCreatingFactoryBean返回的这个ObjectFactory实例可以为我们返回容器管理的相关对象。

ObjectFactoryCreatingFactoryBean 实现了BeanFactoryAware接口,它返回的ObjectFactory实例只是特定于与Spring容器进行交互的一个实现而已。

使用它的好处就是,隔离了客户端对象对BeanFactory的直接引用。现在,我们使用ObjectFactory取得FXNewsBean的实例。

public class MockNewsPersister implements IFXNewsPersister {
    private ObjectFactory newsBeanFactory;

    public void persistNews(FXNewsBean bean) {
        persistNews();
    }

    public void persistNews() {
        System.out.println("persist bean:" + getNewsBean());
    }

    public FXNewsBean getNewsBean() {
        return newsBeanFactory.getObject();
    }

    public void setNewsBeanFactory(ObjectFactory newsBeanFactory) {
        this.newsBeanFactory = newsBeanFactory;
    }
} 

有了以上的类定义之后,我们应该为MockNewsPersister注入相应的ObjectFactory,这也正是ObjectFactoryCreatingFactoryBean闪亮登场的时候

<beans>
    <bean id="newsBean" class="..domain.FXNewsBean" singleton="false"></bean>
    <bean id="newsBeanFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
        <property name="targetBeanName">
            <idref bean="newsBean"/>
        </property>
    </bean>
    <bean id="mockPersister" class="..impl.MockNewsPersister">
        <property name="newsBeanFactory">
            <ref bean="newsBeanFactory"/>
        </property>
    </bean>
</beans> 

也可以使用ServiceLocatorFactoryBean来代替ObjectFactoryCreatingFactoryBean,该FactoryBean可以让我们自定义工厂接口,而不用非要使用Spring的ObjectFactory。可以参照该类定义的Javadoc取得更多信息,Javadoc中有详细的实例,足够让你了解该类的使用和功能。

方法替换

与方法注入只是通过相应方法为主体对象注入依赖对象不同,方法替换更多体现在方法的实现层面上,它可以灵活替换或者说以新的方法实现覆盖掉原来某个方法的实现逻辑。

基本上可以认为,方法替换可以帮助我们实现简单的方法拦截功能。

假设某天我看FXNewsProvider不爽,想替换掉它的getAndPersistNews方法默认逻辑,这时,我就可以用方法替换将它的原有逻辑给替换掉。

FXNewsProviderMethodReplacer类的定义

public class FXNewsProviderMethodReplacer implements MethodReplacer {
    private static final transient Log logger = LogFactory.getLog(FXNewsProviderMethodReplacer.class);

    public Object reimplement(Object target, Method method, Object[] args) throws Throwable {
        logger.info("before executing method[" + method.getName() + "] on Object[" + target.getClass().getName() + "].");
        System.out.println("sorry,We will do nothing this time.");
        logger.info("end of executing method[" + method.getName() + "] on Object[" + target.getClass().getName() + "].");
        return null;
    }
}

有了要替换的逻辑之后,我们就可以把这个逻辑通过配置到FXNewsProvider的bean定义中,使其生效,配置到FXNewsProvider的bean定义中,使其生效

FXNewsProvider中使用方法替换的相关配置

<beans>
    <bean id="djNewsProvider" class="..FXNewsProvider">
        <constructor-arg index="0">
            <ref bean="djNewsListener"/>
        </constructor-arg>
        <constructor-arg index="1">
            <ref bean="djNewsPersister"/>
        </constructor-arg>
        <replaced-method name="getAndPersistNews" replacer="providerReplacer"></replaced-method>
    </bean>
    <bean id="providerReplacer" class="..FXNewsProviderMethodReplacer"></bean>  
<!--其他bean配置-->  ...
</beans>

如果要替换的方法存在参数,或者对象存在多个重载的方法,可以在内部通过明确指定将要替换的方法参数类型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值