上一篇中简单了说了一下使用XML进行bean的配置以及bean的两种注入方式:属性注入,即set方法注入;构造方法注入,即通过指定与构造方法一致的参数个数与类型。在构造方法注入中,可以使用index指定参数位置,index是0从开始的,还可以使用type属性指定参数类型,通过这二者组合,IoC容器就可以明确的知道使用哪个构造方法进行依赖注入。


  但是上面做的那些工作,也只是对单个bean的配置,在我们的复杂的应用系统的实现中,我们往往需要多个bean之间相互协作,共同完成系统的功能。在这一篇文章中你将会找到答案,如何引用其他的bean


 引用其他的bean的方法一般来说有两种:一是通过ref属性或者标签,为bean的属性或者构造参数指定要引用的bean,;二是在使用内部bean,即在属性或者构造方法里面包含bean的声明。


使用ref标签或者属性引用其他bean

  我们结合上一篇的代码,新建一个Person类,Person有一个Car,代码如下,Person类有三个属性:name,age以及car,car是Car类型的:

package com.study.spring;

public class Person {
	private String name;
	private int age;
	private Car car;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Car getCar() {
		return car;
	}
	public void setCar(Car car) {
		this.car = car;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
	}
	public Person() {
		super();
	}
	public Person(String name, int age, Car car) {
		super();
		this.name = name;
		this.age = age;
		this.car = car;
	}
}

  接下来让我们看看如何去配置一个Person类型的bean,下面是配置文件:

<bean id="car2" class="com.study.spring.Car">
	<constructor-arg value="Audi" type="java.lang.String"></constructor-arg>
	<constructor-arg value="Shanghai" type="java.lang.String"></constructor-arg>
	<constructor-arg value="240" type="int"></constructor-arg>
</bean>

<bean id="person" class="com.study.spring.Person">
	<property name="name" value="Aces"></property>
	<property name="age" value="18"></property>
	<!-- 可以使用property的ref属性,建立bean之间的引用关系 -->
	<property name="car" ref="car2"></property>
</bean>

<bean id="person2" class="com.study.spring.Person">
	<property name="name" value="Aces"></property>
	<property name="age" value="18"></property>
	<!-- 可以使用property的子节点ref标签,建立bean之间的引用关系 -->
	<property name="car">
		<ref bean="car2" />
	</property>
</bean>

<bean id="person3" class="com.study.spring.Person">
	<property name="name" value="Aces"></property>
	<property name="age" value="18"></property>
	<!-- 内部bean,不能被外部使用,只能在内部使用 -->
	<property name="car">
		<bean class="com.study.spring.Car">
			<constructor-arg value="Ford" type="java.lang.String"></constructor-arg>
			<constructor-arg value="Changan" type="java.lang.String"></constructor-arg>
			<constructor-arg value="20000" type="double"></constructor-arg>
		</bean>
	</property>
</bean>

  这里定义了三个Person类型的bean:person、person2、person3,这里的ref标签中引用的car2,是上一篇文章中给出的car2的,为了方便大家看清楚,所以这里把car2的定义也给了出来。顺便复习一下,这里的三个Person类型的bean(person、person2、person3)的三个属性都是通过<property>标签进行注入的,即通过属性注入的,而Car类型的bean(car2)是通过<constructor-arg>标签进行注入的,即通过构造方法注入

  可以看到引入其他的bean的配置实在是太简单了,我们可以事先定义好要使用的bean,然后通过ref属性引用,即第一种配置方法;也可以通过<ref>标签,即第二种配置方法。这两种方式都是引用的我们事先定义好的bean,即先有了car2,然后直接通过ref属性进行引用。

  下面看一下我们的第三种配置方式,我们在<property>标签的内部,定义了一个bean,我们把这种bean称之为内部bean,这种bean对外部是不可见的,即外部不能够使用它,相应的也可以不设置id和name属性(因为外面反正也用不了,(*^__^*) 嘻嘻……)。内部bean的定义可以使用我们上一篇文章中说的bean的配置方法进行定义,使用属性注入或者构造方法注入都是可以的。说了这么多,到底管不管用呢,下面看一下程序运行结果(大家看的时候可能看不完全图片,可以点击图片在新窗口看完整图片):

wKiom1VHeKWzS-ahAAG9CFQymFU660.jpg  可以看到person和person2这两个bean的输出中关于car的部分,和car2是一样的,你可能注意到了,price是0.0,这是因为我们的car2中也是0.0,我们就没有给car的price属性赋值,我们的配置文件中第三个参数是int类型的,也就是会赋值给maxSpeed属性。而person3中关于car的输出是和我们内部bean的定义是一致的。


  接下来让我们看一下使用构造方法注入的定义形式:

<!-- 通过构造器来配置bean -->
<bean id="person4" class="com.study.spring.Person">
	<constructor-arg value="Aces"></constructor-arg>
	<constructor-arg value="18"></constructor-arg>
	<constructor-arg ref="car2"></constructor-arg>
	<!-- 
	<constructor-arg>
		<ref bean="car2"/>
	</constructor-arg>
	 -->
</bean>

<!-- 通过构造器来配置bean -->
<bean id="person5" class="com.study.spring.Person">
	<constructor-arg value="Aces"></constructor-arg>
	<constructor-arg value="18"></constructor-arg>
	<!-- 测试赋值null,null必须这么写,是专有标记 -->
	<constructor-arg>
		<null />
	</constructor-arg>
</bean>

<!-- 级联赋值 -->
<bean id="person6" class="com.study.spring.Person">
	<constructor-arg value="Aces"></constructor-arg>
	<constructor-arg value="18"></constructor-arg>
	<constructor-arg ref="car2"></constructor-arg>
	<!-- 级联赋值,其实和属性注入是一样的,也是通过set方法,
		  注意:属性需要先初始化之后才可以对级联属性进行赋值,否则会有异常,
		  和struts2不同,struts2会自动的创建对象并为级联属性赋值
	 -->
	<property name="car.price" value="30000"></property>
</bean>

<!-- 这个bean的配置是有问题的,因为没有对car进行赋值,所以car是null,
	spring不会自动的创建一个car对象并对其赋值,
	所以不能直接对car.price进行级联赋值 

<bean id="person7" class="com.study.spring.Person">
	<property name="name" value="Aces"></property>
	<property name="age" value="18"></property>
	<property name="car.price" value="30000"></property>
</bean>
-->

  这里又给出了4个Person类型的bean的定义,person4、person5、person6、person7。其中person4、person5、person6都是通过构造方法进行注入,可以通过constructor-arg的ref属性,直接指定已经定义的bean,即引用外部bean的方法,也可以使用constructor-arg的子标签<ref>来引用外部的bean,即对应person4的配置方式。

  看一下person5的定义,赋值为null,必须要这么定义,这是null的专有标志。

  看一下person6的定义,因为我们的car2一直没有给price赋值,让人一直觉得很不爽(不知道你有没有觉得不爽,O(∩_∩)O哈哈~),这里就解决了这个问题,我们可以对依赖的bean进行级联赋值,但是需要注意的是:属性需要先初始化之后才可以对级联属性进行赋值,否则会有异常,和struts2不同,struts2会自动的创建对象并为级联属性赋值。下面person7的定义就很好的说明了这个问题,person7的这种定义方式,我们没有为car赋值(其实他就是null),进行级联赋值的时候,就会抛出异常,当然了上面被我注释掉了,就是为了说明这个问题。下面给出一个把person7放开的一个运行结果截图:

wKiom1VHf-zjgKaiAAWpL0AENm8628.jpg可  以看到什么输出都没有,上面的那两个输出是构造方法和set方法中添加的打印语句,因为我们前面已经说过了,IoC容器会在初始化的时候,创建所有的bean,当它创建person7的时候,因为我们没有初始化car属性,而是直接进行级联赋值,所以它抛出了异常。其实不管你是用属性注入还是构造方法注入,他们的结果是一样的,可以按照个人习惯,灵活使用。

  下面给一个person7注释掉之后的运行结果截图:

wKioL1VHhO7BUSwMAAVKTttQBy8265.jpg  不知道大家有没有注意到car2的price已经变成了30000.0了,为什么会这样,什么时候变的?请大家看一下person6的定义,有没有找到?

  可能有些人还在想怎么没有给出测试类,只是给出了测试结果,我开始是认为学习spring的肯定都是有一定java基础的,其实测试类非常简单,与《Spring学习系列之——第一章:Spring版本的HelloWorld》文章中给出的类似,获取bean,然后调用类的打印。下面一并给出来好了,省的大家再去翻找,测试类真心没内容呀。。。。测试类代码如下:

package com.study.spring;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
	public static void main(String[] args) {
		
		//1.创建Spring的IOC容器对象,在创建容器的时候,完成了bean的初始化和属性的设置
		//ApplicationContext代表IOC容器,在初始化上下文的时候就实例化所有单例的bean
		//ClassPathXmlApplicationContext:是ApplicationContext接口的实现类,该类从类路径下来加载配置文件
		//FileSystemXmlApplicationContext:是ApplicationContext接口的实现类,该类从文件系统中加载配置文件
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		
		//2.从IOC容器获取bean实例
		HelloWorld helloWorld = (HelloWorld)ctx.getBean("helloWorld");
		//利用类型返回IOC容器中的bean,但要求IOC容器必须只能有一个该类型的Bean
		//HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
		
		//3.调用bean的方法
		helloWorld.hello();
		
		Car car = (Car)ctx.getBean("car");
		System.out.println(car);
		
		Car car2 = (Car)ctx.getBean("car2");
		System.out.println(car2);
		
		Car car3 = (Car)ctx.getBean("car3");
		System.out.println(car3);
		
		Person person = (Person)ctx.getBean("person");
		System.out.println(person);
		
		
		Person person2 = (Person)ctx.getBean("person2");
		System.out.println(person2);
		
		Person person3 = (Person)ctx.getBean("person3");
		System.out.println(person3);
		
		Person person4 = (Person)ctx.getBean("person4");
		System.out.println(person4);
		
		Person person5 = (Person)ctx.getBean("person5");
		System.out.println(person5);
		
		Person person6 = (Person)ctx.getBean("person6");
		System.out.println(person6);
		
//		Person person7 = (Person)ctx.getBean("person7");
//		System.out.println(person7);
	}
}