Elim的博客

用来记录一些原创性的总结


Elim

1 bean定义

1.1 命名

给bean命名不是必须的,但是如果如果有其它bean需要引用该bean,那么就需要给该bean命名。当我们没有给bean命名时,Spring将自动为该bean生成一个唯一的名称。

1.1.1 id和name属性

可以通过id和name给bean命名,如果只需要给bean指定一个名称则使用id和name都可以。id和name在整个ApplicationContext中都要求是唯一的,其中id我们只能指定一个,如果我们还需要为当前bean指定更多的名称,则可以通过name进行指定。name属性可以用来为bean指定多个名称,多个名称之间可以以逗号“,”、分号“;”或空格进行分隔。

	<bean id="hello" name="hello2,hello3 hello4;hello5" class="java.lang.String">
		<constructor-arg value="hello"/>
	</bean>

1.1.2 alias元素指定别名

alias元素可以为指定的名称指定一个别名。假设在我们的ApplicationContext中拥有一个名为name1的bean,同时通过alias元素指定了name1的别名为alias1,这样在其它需要引用名为name1的bean的地方都可以使用alias1来代替,当然继续使用name1也是可以的。这种机制是非常有效的。考虑这样一种场景,假设我们的应用有两个module,其中module1需要关联名为module1-dataSource,module2需要关联名为module2-dataSource。但实际上它们使用的都是同一个数据源,当然我们可在定义该DataSource时同时指定其名称为module1-dataSource和module2-dataSource。另一种方式是我们可以为该DataSource指定两个别名,分别为module1-dataSource和module2-dataSource。
以下是一个使用alias元素定义别名的示例,其分别指定hello的别名为helloAlias1和helloAlias2。

	<alias name="hello" alias="helloAlias1"/>
	<alias name="hello" alias="helloAlias2"/>
	
	<bean name="hello" class="java.lang.String">
		<constructor-arg value="hello"/>
	</bean>

1.2 class属性指定类型

在定义bean时可以通过class属性来指定当前bean对应的类型。class属性要求是对应Class类的全限定名,即需要包含包名的。如果我们需要定义的bean类型属于某个类的静态内部类,那么对应的class值应该是该内部类的二进制名称,即中间以$符号连接。下面来看一个示例,假设我们有如下这样一个类,先需要分别定义Hello对应的bean和World对应的bean,其中Hello所在的包名为com.app

public class Hello {

	public static class World {
		
	}
	
}

那么,对应bean定义应该是这样子的:

	<bean id="hello" class="com.app.Hello"/>

	<bean id="world" class="com.app.Hello$World"/>

1.3 depends-on

depends-on是定义在bean元素上的一个属性,用于指定其所依赖的bean,这样Spring将在初始化当前bean前先初始化其depends-on属性所指定的bean。所以depends-on的功能就是指定一个bean所依赖的bean,通过它来告诉Spring将优先初始化depends-on属性指定的bean。注意,该属性只有告诉Spring该优先初始化哪个bean的功能,Spring不会因为这个属性的定义而注入对应的bean。在不使用depends-on属性的情况下,Spring初始化bean的顺序是固定的,通常是根据bean定义的先后顺序来进行初始化。在下面的示例中,Spring将先初始化world,再初始化hello,但是其不会将world注入给hello。

	<bean name="world" class="com.app.World"/>
	<!-- 通过depends-on属性告诉Spring先初始化depends-on属性指定的bean,即这里的world -->
	<bean id="hello" class="com.app.Hello" depends-on="world"/>

如果我们需要指定多个bean先于某个bean被初始化,我们也可以通过depends-on属性来指定,只是此时多个bean之间需要以逗号、分号或空格进行分隔。如下示例中就表示bean1依赖于bean2、bean3和bean4,即bean2、bean3和bean4需先于bean1被初始化。

	<!-- 当依赖于多个bean时,多个bean之间以逗号、分号或空格进行分隔 -->
	<bean id="bean1" class="com.app.Bean1" depends-on="bean2,bean3,bean4"/>

使用depends-on属性后,depends-on属性指定的bean将先于该bean被初始化,但对于销毁而言,其是一个逆过程,即将先销毁该bean,再销毁depends-on属性指定的bean。

1.4 lazy-init

默认情况下,Spring将在初始化bean容器的时候初始化所有的单例bean。如果我们有一个bean是单例的,但是又不想其在bean容器初始化的时候被初始化,那么我们可以在该bean上定义lazy-init属性为true,这样Spring将不会在初始化bean容器的时候将其初始化。相对应的,其会在第一次被需要的时候进行初始化。所以如果我们有一个叫beanA的单例bean定义了lazy-init为true,这就表示其不会在Spring初始化bean容器的时候进行初始化,但是如果其被另一个名叫beanB的单例bean所依赖,而beanB没有指定lazy-init为true,这就意味着beanB将在Spring初始化bean容器时进行初始化,这个时候由于Spring需要给beanB注入一个beanA,也就是说beanA在这个时候被需要了,所以此时Spring也将会初始化beanA。

	<!-- 通过lazy-init="true"指定其将不在初始化bean容器时初始化 -->
	<bean id="beanA" class="com.app.BeanA" lazy-init="true"/>
	<!-- 由于beanB对beanA有依赖,所以在初始化BeanB后,如果容器中不存在beanA,Spring将初始化备案A -->
	<bean id="beanB" class="com.app.BeanB">
		<property name="beanA" ref="beanA"/>
	</bean>

lazy-init的值默认是false,也就是说对于单例bean而言,如果没有指定lazy-init=”true”时其默认会在bean容器初始化时被初始化,如果用户希望这种全局的初始化策略为不在bean容器初始化时进行初始化,则可以在beans元素上定义default-lazy-init=”true”

1.5 继承bean定义(parent)

试想一下,假设我们有一个类ParentA,其需要引用BeanA、BeanB和BeanC这三种类型的对象。然后针对于ParentA而言,我们又拥有三个子类ChildA、ChildB和ChildC。如果我们现在要定义ChildA、ChildB和ChildC三种类型的bean,依据传统的思想是不是就需要定义这三个bean,然后分别给这三个bean注入类型为BeanA、BeanB和BeanC的bean呢?Spring为我们提供了一种继承机制,它允许我们在定义一个bean时指定其对应的父bean,然后子bean将继承父bean的属性等,当然通过子bean注入的内容将覆盖父bean的内容,这里所谓的继承是对应bean定义的继承,不是真正意义上对象的继承。Spring允许我们在定义一个bean时通过parent属性来指定对应的父bean定义,其属性值对应父bean的id或name。所以针对于该应用场景,我们可以使用Spring的继承机制进行如下定义。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="beanA" class="com.app.BeanA"/>
    <bean id="beanB" class="com.app.BeanB"/>
    <bean id="beanC" class="com.app.BeanC"/>
    
	<bean id="parentA" class="com.app.ParentA">
		<property name="beanA" ref="beanA"/>
		<property name="beanB" ref="beanB"/>
		<property name="beanC" ref="beanC"/>
	</bean>
	
	<bean id="childA" class="com.app.ChildA" parent="parentA"/>
	<bean id="childB" class="com.app.ChildB" parent="parentA"/>
	<bean id="childC" class="com.app.ChildC" parent="parentA"/>
	
</beans>

这时子bean也可以注入自己额外的内容,比如对于childA而言,它可以给自己注入一个beanD,这是没有问题的。当然,它也可以选择给自己注入一个特定的BeanA,此时childA自己注入的BeanA将覆盖其父bean持有的BeanA,即childA所拥有的BeanA将是其自身注入的那一个。
对于上述配置而言,将默认初始化所有的单例bean,所以上述配置中的parentA也将被初始化,如果我们不希望其被初始化,或者parentA是一个抽象类,根本就不能被初始化,我们可以在该bean上通过abstract=”true”指定该bean是抽象的,这样Spring将不会对其进行初始化,而仅仅把它当做其它bean定义的父bean定义而已。

	<bean id="parentA" class="com.app.ParentA" abstract="true">
		<property name="beanA" ref="beanA"/>
		<property name="beanB" ref="beanB"/>
		<property name="beanC" ref="beanC"/>
	</bean>

如果我们的父bean不需要真正被初始化,只是用来作为一个父bean定义供子bean继承时,我们也可以不指定其对应的class,但需要指定abstract=”true”。这对实际应用是非常有用的,打个比方,如果我们的应用是通过XML配置来管理对应的依赖关系的,同时我们的Dao都需要注入一个JdbcTemplate,我们就无需在定义Dao对应bean时给每个bean都定义注入一个JdbcTemplate,而可以让它们都继承一个父级的bean定义,然后在父级的bean定义上去注入对应的JdbcTemplate。如:

	<bean id="parent" abstract="true">
		<property name="jdbcTemplate" ref="jdbcTemplate"/>
	</bean>
	<bean id="xxxDao" class="xxx.xxx.xxxDao" parent="parent"/>

1.6 description

通过description元素指定当前bean的描述信息,方便用户阅读。

	<bean id="hello" class="com.app.Hello">
		<description>当前bean的描述信息,便于阅读</description>
	</bean>

(注:本文是基于Spring4.1.0所写)