见贤思齐焉,见不贤而内省也
接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>
<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)类型,比如int、Integer等。
(2)<ref>
使用ref来引用容器中其他的对象实例,可以通过ref的local、parent和bean属性来指定引用的对象的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种自动绑定模式,即no、byName、byType、constructor和autodetect,下面是它们的具体介绍。
- 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定义中寻找类型为Bar的bean定义。如果找到,则将找到的bean绑定到Foo的bean定义;如果没有找到,则不做设置。但如果找到多个,容器会告诉你它解决不了“该选用哪一个”的问题,你只好自己查找原因,并自己修正该问题。
- constructor
byName和byType类型的自动绑定模式是针对property的自动绑定,而constructor类型则是针对构造方法参数的类型而进行的自动绑定,它同样是byType类型的绑定模式。不过,constructor是匹配构造方法的参数类型,而不是实例属性的类型。与byType模式类似,如果找到不止一个符合条件的bean定义,那么,容器会返回错误。
- autodetect
- autodetect
这种模式是byType和constructor模式的结合体,如果对象拥有默认无参数的构造方法,容器会优先考虑byType的自动绑定模式。否则,会使用constructor模式。
注意:自 动绑 定 只 应 用于 “ 原 生 类型 、String类 型 以 及 Classes类型 以 外 ”的 对 象 类 型, 对“原生类型、String类型和Classes类型”以及“这些类型的数组”应用自动绑定是无效的
6.dependency-check
- none。不做依赖检查。将dependency-check指定为none跟不指定这个属性等效,所以,还是不要多敲那几个字符了吧。默认情况下,容器以此为默认值。
- simple。如果将dependency-check的值指定为simple,那么容器会对简单属性类型以及相关的collection进行依赖检查,对象引用类型的依赖除外。
- object。只对对象引用类型依赖进行检查。
- all。将simple和object相结合,也就是说会对简单属性类型以及相应的collection和所有对象引用类型的依赖进行检查。
7. lazy-init
延迟初始化(lazy-init)这个特性的作用,主要是可以针对ApplicationContext容器的bean初始化行为施以更多控制。与BeanFactory不同,ApplicationContext在容器启动的时候,就会马上对所有的“singleton的bean定义”①进行实例化操作。通常这种默认行为是好的,因为如果系统有问题的话,可以在第一时间发现这些问题,但有时,我们不想某些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>
上面就是模板化的配置, newsProviderTemplate的bean定义通过abstract属性声明为true,说明这个bean定义不需要实例化。实际上,这就是之前提到的可以不指定class属性的少数场景之一(当然,同时指定class和abstract="true"也是可以的)。该bean定义只是一个配置模板,不对应任何对象。
总结:都是理论的东西,基础的东西,看一看,翻一翻就行了