bean标签的配置大致可以分为五类
- Bean命名的三种方式
- Bean创建的三种方式
- Bean注入依赖的三种方式
- Bean属性值设置,普通类型,集合类型,Empty & NULL
- 杂项
原著中bean的配置主要分为四个部分,
- A package-qualified class name:bean对应类的名称
- Bean behavioral configuration elements:bean的行为和状态。包括bean的生命周期,作用域等
- References to other beans that are needed for the bean to do its work:bean的依赖。
- Other configuration settings to set in the newly created objects:其他配置项,例如bean的一些非必备属性。
此部分内容为官网的1.3和1.4小节的阅读笔记。
1、命名
Bean的命名有三种方式,id,name,alias。
- id:bean的唯一标识,不能重复,一般都是使用此方式为bean命名。
- name:bean的名称,IOC容器允许bean的名称重复,一个bean可以拥有多个名称。不指定时,类名称首字母小写作为name属性的默认值。
- alias:bean的别名,一个类可以有多个别名。别名最好有意义。
<!-- naming bean, bean的命名 --> <!-- id方式 --> <bean id="user" class="learn.springcore.bean.User"/> <!-- name方式 --> <bean name="manager" class="learn.springcore.bean.User"/> <bean name="vip" class="lean.springcore.bean.User"/> <!-- alias方式 --> <alias name="manager" alias="boss"/> <alias name="manager" alias="projectManager"/>
2、创建方式
在spring的语义中,创建bean指将bean注入到IOC容器中,由spring根据配置信息创建对象。
Bean的创建方式有三种,构造器方式,静态工厂方法模式,非静态工厂方法模式。
2.1 构造器
当使用类的默认构造器时,设置bean的class属性为类全名即可。
当使用类的其他构造器时,需要在bean内部配置constructor-arg标签,它代表构造器的参数。参数的映射方式有三种方式,参数的顺序,参数的名称,参数的类型。
// 假设用户存在三个构造器,默认构造器,User(String name),User(String name, int age) <!-- naming bean, bean的命名 --> <!-- 默认构造器方式,class属性为类全名即可 --> <bean id="user" class="learn.springcore.bean.User" /> <!--只有单个名称的构造器,使用参数名称映射,name属性的值为参数的名称 --> <bean id="user" class="learn.springcore.bean.User"> <constructor-arg name="name" value="jack" /> </bean> <!-- 第三个构造器,使用参数顺序映射,0映射为名称参数,1映射为年龄参数 --> <bean id="user" class="learn.springcore.bean.User"> <constructor-arg index="0" value="jack" /> <constructor-arg index="1" value="18" /> </bean> <!-- 第三个构造器,使用参数类型映射,参数的数据类型不予许重复,string映射为名称参数,int映射为年龄参数 --> <bean id="user" class="learn.springcore.bean.User"> <constructor-arg type="java.lang.String" value="jack" /> <constructor-arg type="java.lang.Integer" value="18" /> </bean>
2.2 工厂静态方法
工厂静态方法方式下,class属性为工厂方法的类全名,factory-method为静态方法名称,它的返回值为注入bean的类型
<!-- 静态工厂方式 --> <bean id="user" class="learn.springcore.factory.UserFactory" factory-method="getInstance"/>
此时UserFactory类存在getInstance方法,它必须是static修饰,返回值为User对象。
2.3 非静态方法
非静态方法方式,class属性不指定,factory-bean属性为工厂类bean标签的ID或者name属性,factory-method为工厂类的方法。此时工厂类必须已注入到容器中。
<!-- 非静态工厂方法方式 --> <bean id="user" factory-bean="userFactory" factory-method="getInstance"/>
3、依赖关系
配置bean依赖关系的有三种方式,
- 构造器方式,上面提到过,不再重复。
- set方法方式。
- 工厂方法的参数方式。
3.1 Set方式
通过Set方式配置依赖关系,本质是配置类的属性,它是将bean标签内部的property标签映射为对象的属性。其中property标签的name属性对应属性名称,value属性相当于给对象的属性赋值。
<!-- 配置用户 --> <bean id="user" class="learn.springcore.bean.User"> <!—user的age属性值为21--> <property name="age" value="21" /> </bean>
age对应User的age属性,value对应age的值,由于age数据类型是Integer,它的映射过程伴随着string--->int的类型转换。
它与构造器方式的区别在于,
- Set方式本质是调用类的set方法,构造器本质是调用类的构造器,调用set方法总是发生在调用构造器之后。
- 如果在创建对象时,存在必备属性,建议使用构造器方式,否则选用Set方式。
// 构造器方式等价于,必备属性是指在对象创建时,必须指定。 User user = new User(); // Set方式等价于 User user1 = new User(); user1.setName("name");
3.2 工厂方法的参数
通过工厂方法参数配置依赖关系,本质是使用工厂静态方法或者是非静态方法创建bean,在其内部使用constructor-arg标签,其中的name属性为参数的名称,value为参数的值。
constructor-arg也可以使用其他方式,例如参数的顺序,参数的类型。
4、属性值
4.1 普通
当为基本数据类型时,使用property子标签,其中name为属性名,value为属性值
<property name=”fieldName” value=”fieldVal”>
当为对象时,使用ref子标签,其中bean为对象的名称(ID,name,alias中的任意一个)。此时bean引用的对象必须已注入到容器中。
<property name=”fieldName” ref=”refBeanID”>
4.2 集合
这部分内容是讲,当属性的数据类型为集合相关类时,如何建立XML配置文件与属性的映射关系,name属性映射为对象属性的名称,在映射属性值时,存在差异,示例如下。
4.2.1 List
<property name="someList"> <list> <value>element</value> <ref bean="date"></ref> </list> </property>
4.2.2 Set
<property name="someSet"> <set> <value>element</value> <ref bean="date"></ref> </set> </property>
4.2.3 Map
<property name="someMap"> <map> <entry key="entry" value="just something"/> <entry key="birthDate" value-ref="date"/> </map> </property>
4.2.4 Properties
<property name="someProperties"> <props> <prop key="name">jack</prop> <prop key="age">21</prop> </props> </property>
4.3 NULL
当属性值为null时,直接使用null标签,当为空字符串时,标签的value属性值为空串。示例如下:
<property name="nullProp"> <null/> </property> <property name="name" value=""/>
5、杂项
5.1 lazy-init
默认情况下,伴随着容器的创建,会触发容器中所有bean的创建。若想实现按需创建,即在获取该bean的时,才触发bean的创建过程,此时可以设置lazy-init的值为true。
当bean被其他bean依赖时,该属性会被忽略。例如beanA依赖bean B,此时设置beanB的lazy-init属性为true。容器的创建过程会触发beanA的创建过程,而beanA的创建过程会触发所有它依赖类的创建过程,此时即使beanB的lazy-init为true,它也会被创建。
5.2 depends-on
当bean B的创建过程作为bean A创建过程的前置条件时,并且此时A与B不存在依赖关系。即要保证bean B在bean A 之前被创建。此时可以通过设置beanA的depends-on属性,它的值为beanB的ID,此时在创建beanA时,总会先创建beanB,保证创建bean的顺序。
这种情况通常发生在bean之间存在间接依赖关系。
5.3 autowire
当配置依赖关系时,通过constructor,set,工厂方法参数都需要手动配置它们之间的依赖关系,例如property标签的name属性映射为对象的属性名称。
spring提供一种自动配置方式,建立配置文件到对象属性之间的映射关系。它有四种方式。
- No:相当于禁用自动配置功能,所有的依赖都需要手动去配置。
- byName:根据属性名称,例如当User对象存在date属性时,此时若容器中存在id为date的bean标签,会自动获取date,并将返回的对象作为User对象date属性的值
<bean id="user" class="learn.springcore.bean.User" p:birthDate-ref="date"/> <bean id="date" class="java.util.Date"/>
这种方式使用ref引用到id为date的bean标签。手动配置。
<bean id="user" class="learn.springcore.bean.User" /> <bean id="birthDate" class="java.util.Date"/>
此时在创建User对象,设置birthDate属性时,会获取标识为birthDate的bean。并将返回的对象作为birthDate的属性值。标识可以为id,name,alias。
-
byType:根据属性的数据类型,例如User对象存在birthDate属性,它的数据类型为java.util.Date时,当容器中存在java.util.Date类型的对象时,会将该对象作为属性值。
-
constructor:与byType相似,只不过它只适用于构造器的参数。
无论是byType,还是byName,当容器中存在多个匹配项时,会报错。
5.4 autowire-candidate
当启用自动注入功能时(autowire的属性值不为no),需要查找匹配的bean时,可以设置当前bean标签autowire-candidate属性为false,忽略当前bean。
例如指定自动注入方式为byName时,创建User对象,设置birthDate属性的值,此时会在容器中查询标识为birthDate的bean,当设置autowire-candidate属性为false时,
<bean id="birthDate" class="java.util.Date" autowire-candidate="false"/>,查找过程会忽略该bean。
6、P 前缀 & C前缀
P字母代表property,它是属性的意思,为了更方便配置bean的属性
C字母代表constructor,它是构造器的意思,为了更方便配置bean构造器的参数。
使用它们,首先需要引入schema。
<!-- p的schema--> xmlns:p="http://www.springframework.org/schema/p" <!-- c的schema--> xmlns:c="http://www.springframework.org/schema/c"
其次需要建立映射关系,
当属性为基本数据类型时,p:propName它会映射为对象的propName属性,它的值为属性的值。
当属性为引用数据类型时,p:propName-ref它会映射为对象的propName属性,它的值为容器中引用对象bean的ID或name。
7、讨论
问题1:循环依赖问题?
答:假设存在A依赖B,B又依赖A的情形,
当它们以构造器方式去配置依赖关系时,会存在错误,这是因为相互在等待对方构造器的调用。
当以Set方式去配置时,此时需要首先去创建对象A,或者是对象B,然后在调用它的set方法,此时不会出错,是因为IOC容器首先会去调用A的构造器,或者是B的构造器,然后调用a.setB或者是b.setA,这两个方法之间不存在任何关系,完全可以把构造器的返回的对象A或者B作为这两个方法的参数。
当以工厂方法参数去配置时,是同理的,因为彼此可以调用默认的构造器,在调用完构造器之后,然后才去调用工厂方法。
问题2:映射问题
答:bean本质上就是对应一个Java类,配置bean的过程本质就是将bean映射为Java对象的过程。有四种类型的映射,
- Bean与Java类的映射关系,通过读取配置信息,如何创建对应的Java类
- Bean标签属性或者是内部属性与Java类属性的映射关系,通过读取配置信息,如何设置对象类的属性
- Bean的value,ref如何转换为需要的Java数据类型。通过读取配置信息,如何转换为属性的数据类型。
- 参数的映射过程。构造器的参数,工厂方法的参数,这些映射方式都有三种方式,根据参数的顺序,参数的名称,参数的类型。