【Spring揭秘】------ 第四章学习 Spring的IoC容器之BeanFactory 02

见贤思齐焉,见不贤而内省也

接01

4.3 BeanFactory的XML之旅

4.3.1 <beans>和<bean>
DTD规定:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
...
</beans>
XSD规定:
<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"
       xmlns:jaxws="http://cxf.apache.org/jaxws"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://cxf.apache.org/jaxws
        http://cxf.apache.org/schemas/jaxws.xsd">
    
</beans>


所有注册到容器的业务对象,在Spring中称之为Bean。所以,每一个对象在XML中的映射也自然而然地对应一个叫做<bean>的元素。既然容器最终可以管理所有的业务对象,那么在XML中把这些叫做<bean>的元素组织起来的, 就叫做<beans>。多个<bean>组成一个<beans>很容易理解,不是吗?
1.<beans>是XML配置文件中最顶层的元素


 default-lazy-init。 其值可以指定为true或者false,默认值为false。用来标志是否对所有的<bean>进行延迟初始化。
 default-autowire。 可以取值为no、 byName、 byType、 constructor以及autodetect。默认值为no,如果使用自动绑定的话,用来标志全体bean使用哪一种默认绑定方式。
 default-dependency-check。 可以取值none、 objects、 simple以及all,默认值为none即不做依赖检查。
 default-init-method。 如果所管辖的<bean>按照某种规则,都有同样名称的初始化方法的话,可以在这里统一指定这个初始化方法名,而不用在每一个<bean>上都重复单独指定。
 default-destroy-method。 default-init-method相对应,如果所管辖的bean有按照某种规则使用了相同名称的对象销毁方法,可以通过这个属性统一指定。
2.<description>、<import>和<alias>
之所以把这几个元素放到一起讲解,是因为通常情况下它们不是必需的。因此这里就了解一下!
  • <description>可以通过<description>在配置的文件中指定一些描述性的信息。通常情况下,该元素是省略的。当然,如果愿意, <description>随时可以为我们效劳。
  • <import>通常情况下,可以根据模块功能或者层次关系,将配置信息分门别类地放到多个配置文件中。在 5想加载主要配置文件,并将主要配置文件所依赖的配置文件同时加载时,可以在这个主要的配置文件中通过<import>元素对其所依赖的配置文件进行引用。比如,如果A.xml中的<bean>定义可能依赖B.xml中的某些<bean>定义,那么就可以在A.xml中使用<import>B.xml引入到A.xml,以类似于<import resource="B.xml"/>的形式。但是, 这个功能在我看来价值不大,因为容器实际上可以同时加载多个配置,没有必要非通过一个配置文件来加载所有配置。不过,或许在有些场景中使用这种方式比较方便也说不定。
  • <alias>可以通过<alias>为某些<bean>起一些“外号”(别名),通常情况下是为了减少输入。比如,假设有个<bean>,它的名称为dataSourceForMasterDatabase,你可以为其添加一个<alias>,像这样<alias name="dataSourceForMasterDatabase" alias="masterDataSource"/>。以后通过dataSourceForMasterDatabase或者masterDataSource来引用这个<bean>都可以,只要你觉得方便就行。
4.3.2 孤孤单单一个人
这里开始介绍Bean中的属性
  • id属性:每个注册到容器的对象都需要一个唯一标志来将其与“同处一室”的“兄弟们”区分开来,就好像我们每一个人都有一个身份证号一样(重号的话就比较麻烦)也有不需要id的,内部<bean>以及不需要根据beanName明确依赖关系的场合
  • name属性:id属性相比, name属性的灵活之处在于, name可以使用id不能使用的一些字符,比如/。而且还可以通过逗号、空格或者冒号分割指定多个name。 name的作用跟使用<alias>id指定多个别名基本相同:
        <bean id="carAssemblyListener" name="/new/superCar" class="com.xservice.h3c.spring.chapter03.SportCarListener">
        </bean>
        <alias name="carAssemblyListener" alias="/my/carAssembly"/>
        <alias name="sellModel" alias="/my/sellModel"/>
  • class属性:这个指定类路径,必须,不然鬼才知道你这个名字对应的是哪个类。

4.3.3 Help Me,Help You(这句话类比多个<bean>之间的关系:互相依赖,互相帮助完成同一目标)

1.构造方法注入的XML之道
<span style="font-weight: normal;">    <bean id="carFactory" class="com.xservice.h3c.spring.chapter03.CarFactory">
        <constructor-arg index="0">
            <ref bean="sellModel" />
        </constructor-arg>
        <constructor-arg index="1">
            <ref bean="/new/superCar" />
        </constructor-arg>
    </bean>
    <bean id="carFactory" class="com.xservice.h3c.spring.chapter03.CarFactory">
        <constructor-arg ref="sellModel" />
        <constructor-arg ref="/new/superCar" />
    </bean></span>
type属性
public class MockBusinessObject {

    private String dependency1;
    private int dependency2;

    public MockBusinessObject(String dependency) {
        this.dependency1 = dependency;
    }

    public MockBusinessObject(int dependency) {
        this.dependency2 = dependency;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("dependency1", dependency1)
                .append("dependency2", dependency2).toString();
    }

}
    <bean id="mockBO" class="com.xservice.h3c.spring.chapter04.MockBusinessObject">
        <constructor-arg>
            <value>111111</value>
        </constructor-arg>
    </bean>
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
        MockBusinessObject mockBO = (MockBusinessObject) applicationContext.getBean("mockBO");
        System.out.println(mockBO.toString());
    }
输出:com.xservice.h3c.spring.chapter04.MockBusinessObject@5b84a312[dependency1=111111,dependency2=0]
修改配置文件如下:
    <bean id="mockBO" class="com.xservice.h3c.spring.chapter04.MockBusinessObject">
        <constructor-arg type="int">
            <value>111111</value>
        </constructor-arg>
    </bean>
输出:com.xservice.h3c.spring.chapter04.MockBusinessObject@17945ecb[dependency1=<null>,dependency2=111111]
index属性:
首先修改MockBusinessObject类:
public class MockBusinessObject {

    private String dependency1;
    private String dependency2;

    public MockBusinessObject(String dependency1, String dependency2) {
        this.dependency1 = dependency1;
        this.dependency2 = dependency2;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("dependency1", dependency1)
                .append("dependency2", dependency2).toString();
    }
    
}
配置文件改成:
    <bean id="mockBO" class="com.xservice.h3c.spring.chapter04.MockBusinessObject">
        <constructor-arg value="111111" />
        <constructor-arg value="222222" />
    </bean>
输出:com.xservice.h3c.spring.chapter04.MockBusinessObject@34e475e1[dependency1=111111,dependency2=222222]
而index可以指定顺序(索引位置从0开始):
    <bean id="mockBO" class="com.xservice.h3c.spring.chapter04.MockBusinessObject">
        <constructor-arg index="1" value="111111" />
        <constructor-arg index="0" value="222222" />
    </bean>
输出:com.xservice.h3c.spring.chapter04.MockBusinessObject@4d593869[dependency1=222222,dependency2=111111]
2.setter方法注入的XML之道
类需要有默认的构造函数,并且提供属性的set方法。我代码测试了下,有set方法就可以把值设置进去,没问题。
配置文件形式:
    <bean id="carFactory" class="com.xservice.h3c.spring.chapter03.CarFactory">
        <property name="carAssemblyListener"  >
            <ref bean ="/new/superCar" />
        </property>
        <property name="sellModel"  >
            <ref bean ="sellModel" />
        </property>
    </bean>
    <bean id="carFactory" class="com.xservice.h3c.spring.chapter03.CarFactory">
        <property name="carAssemblyListener" ref="/new/superCar" />
        <property name="sellModel" ref="sellModel" />
    </bean>
3.<property>和<constructor-arg>
(1)<value>
可以通过value为主体对象注入简单的数据类型,不但可以指定String类型的数据,而且可以指定其他Java语言中的原始类型以及它们的包装器(wrapper)类型,比如intInteger等。
(2)<ref>
使用ref来引用容器中其他的对象实例,可以通过reflocalparentbean属性来指定引用的对象的beanName是什么。
  • local:只能指定与当前配置的对象在同一个配置文件的对象定义的名称(可以获得XML解析器id约束验证支持)
  • parent:则只能指定位于当前容器的父容器中定义的对象引用
  • bean:基本上通吃,所以,通常情况下,直接使用bean来指定对象引用就可以了
(3)<idref>
如果要为当前对象注入所依赖的对象的名称,而不是引用,可以使用<value>,不过使用idref后,容器在解析配置的时候就可以帮4你检查这个beanName到底是否存在,而不用等到运行时才发现这个beanName对应的对象实例不存在
(4)内部<bean>
使用<ref>可以引用容器中独立定义的对象定义。但有时,可能我们所依赖的对象只有当前一个对象引用,或者某个对象定义我们不想其他对象通过<ref>引用到它。这时,我们可以使用内嵌的<bean>,将这个私有的对象定义仅局限在当前对象
(5)<list>、(6)<Set>、(7)<Map>
三个放一起看了。
   <property name="param1">
        <list>
            <value> something</value>
            <ref bean="someBeanName"/>
            <bean class="..."/>
        </list>
    </property>
    <property name="param2">
        <list>
            <value>stringValue1</value>
            <value>stringValue2</value>
        </list>
    </property>
    <property name="mapping">
        <map> 
            <entry key="strValueKey">
                <value>something</value>
            </entry> 
            <entry>
                <key>objectKey</key>
                <ref bean="someObject"/>
            </entry> 
            <entry key-ref="lstKey">
                <list>
                    ... 10
                </list>
            </entry>
            ...
        </map> 
    </property>
对于map,可以嵌套任意多个<entry>,每个都需要制定一个键和值。
(8)<props>
只能指定String类型的键(key)和值
public class MockDemoObject2 {

    private Properties emailAddrs;
    // 必要的setter和getter方法

    public Properties getEmailAddrs() {
        return emailAddrs;
    }

    public void setEmailAddrs(Properties emailAddrs) {
        this.emailAddrs = emailAddrs;
    }

    @Override
    public String toString() {
        return "MockDemoObject2{" +
                "emailAddrs=" + emailAddrs.toString() +
                '}';
    }

}
    <bean id="mockDO2" class="com.xservice.h3c.spring.chapter04.MockDemoObject2">
        <property name="emailAddrs">
            <props>
                <prop key="author">fujohnwang@gmail.com</prop>
                <prop key="support">support@spring21.cn</prop>
            </props>
        </property>
    </bean>
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
        MockDemoObject2 mockDO2 = (MockDemoObject2) applicationContext.getBean("mockDO2");
        System.out.println(mockDO2.toString());
    }
输出:MockDemoObject2{emailAddrs={author=fujohnwang@gmail.com, support=support@spring21.cn}}
(9)<null/>
只是表示一个空元素,用到的场景也不多,跳过
4.depends-on
常情况下,可以直接通过之前提到的所有元素,来显式地指定bean之间的依赖关系。这样,容器在初始化当前bean定义的时候,会根据这些元素所标记的依赖关系,首先实例化当前bean定义所依赖的其他bean定义。但是,如果某些时候,我们没有通过类似<ref>的元素明确指定对象A依赖于对象B的话,如何让容器在实例化对象A之前首先实例化对象B呢?
    <bean id="classAInstance" class="...ClassA" depends-on="configSetup"/>
    <bean id="configSetup" class="SystemConfigurationSetup"/>
depends-on还可以用逗号分隔各个beanName
5. autowire
通过autowire属性,可以指定当前bean定义采用某种类型的自动绑定模式。Spring提供了5种自动绑定模式,即nobyNamebyTypeconstructorautodetect,下面是它们的具体介绍。
  • no-容器默认的自动绑定模式,也就是不采用任何形式的自动绑定,完全依赖手工明确配置各个bean之间的依赖关系,以下代码演示的两种配置是等效的:
    <bean id="beanName" class="..."/>
    <bean id="beanName" class="..." autowire="no"/>
  • byName
    public class Foo
    {
        private Bar emphasisAttribute;
        ...
        // 相应的setter方法定义
    }
    public class Bar
    {
        ...
    }
    <bean id="fooBean" class="...Foo" autowire="byName">
    </bean>
    <bean id="emphasisAttribute" class="...Bar">
    </bean>
第二个bean定义的id为emphasisAttribute,与Foo类中的实例变量名相同,自动绑定
  • byType
对于byName模式中的实例类Foo来说,容器会在其所管理的所有bean定义中寻找类型为Barbean定义。如果找到,则将找到的bean绑定到Foobean定义;如果没有找到,则不做设置。但如果找到多个,容器会告诉你它解决不了“该选用哪一个”的问题,你只好自己查找原因,并自己修正该问题。
  • constructor
byNamebyType类型的自动绑定模式是针对property的自动绑定,而constructor类型则是针对构造方法参数的类型而进行的自动绑定,它同样是byType类型的绑定模式。不过,constructor是匹配构造方法的参数类型,而不是实例属性的类型。与byType模式类似,如果找到不止一个符合条件的bean定义,那么,容器会返回错误。
  • autodetect
  • autodetect
这种模式是byTypeconstructor模式的结合体,如果对象拥有默认无参数的构造方法,容器会优先考虑byType的自动绑定模式。否则,会使用constructor模式。
注意:
自 动绑 定 只 应 用于 “ 原 生 类型 、String类 型 以 及 Classes类型 以 外 ”的 对 象 类 型, 对“原生类型、String类型和Classes类型”以及“这些类型的数组”应用自动绑定是无效的
6.dependency-check
  • none。不做依赖检查。将dependency-check指定为none跟不指定这个属性等效,所以,还是不要多敲那几个字符了吧。默认情况下,容器以此为默认值。
  • simple。如果将dependency-check的值指定为simple,那么容器会对简单属性类型以及相关的collection进行依赖检查,对象引用类型的依赖除外。
  • object。只对对象引用类型依赖进行检查。
  • all。将simpleobject相结合,也就是说会对简单属性类型以及相应的collection和所有对象引用类型的依赖进行检查。
7. lazy-init
延迟初始化(lazy-init)这个特性的作用,主要是可以针对ApplicationContext容器的bean初始化行为施以更多控制。与BeanFactory不同,ApplicationContext在容器启动的时候,就会马上对所有的“singletonbean定义”进行实例化操作。通常这种默认行为是好的,因为如果系统有问题的话,可以在第一时间发现这些问题,但有时,我们不想某些bean定义在容器启动后就直接实例化,可能出于容器启动时间的考虑,也可能出于其他原因的考虑。总之,我们想改变某个或者某些bean义在ApplicationContext容器中的默认实例化时机。这时,就可以通过<bean>lazy-init属性来控制这种初始化行为 题的话,可以在第一时间发现这些问题,但有时,我们不想某些 bean定义在容器启动后就直接实例化,可能出于容器启动时间的考虑,也可能出于其他原因的考虑。总之,我们想改变某个或者某些bean义在ApplicationContext容器中的默认实例化时机。这时,就可以通过<bean>lazy-init属性来控制这种初始化行为
4.3.4 继承?我也会!
这块内容讲到了继承和模板化,下面针对这两个展示下代码:
    <bean id="superNewsProvider" class="..FXNewsProvider">
        <property name="newsListener">
            <ref bean="djNewsListener"/>
        </property>
        <property name="newPersistener">
            <ref bean="djNewsPersister"/>
        </property>
    </bean>
    <bean id="subNewsProvider" class="..SpecificFXNewsProvider">
        <property name="newsListener">
            <ref bean="specificNewsListener"/>
        </property>
        <property name="newPersistener">
            <ref bean="djNewsPersister"/>
        </property>
    </bean>
改成:
    <bean id="superNewsProvider" class="..FXNewsProvider">
        <property name="newsListener">
            <ref bean="djNewsListener"/>
        </property>
        <property name="newPersistener">
            <ref bean="djNewsPersister"/>
        </property>
    </bean>
    <bean id="subNewsProvider" parent="superNewsProvider"
          class="..SpecificFXNewsProvider">
        <property name="newsListener">
            <ref bean="specificNewsListener"/>
        </property>
    </bean>
上面配置展示了继承的使用,减少了配置的冗余。
    <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>
上面就是模板化的配置, newsProviderTemplatebean定义通过abstract属性声明为true,说明这个bean定义不需要实例化。实际上,这就是之前提到的可以不指定class属性的少数场景之一(当然,同时指定classabstract="true"也是可以的)。该bean定义只是一个配置模板,不对应任何对象。

总结:都是理论的东西,基础的东西,看一看,翻一翻就行了









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值