概述
在学习 bean 的加载过程时不了解 bean 循环依赖的概念,因此在查阅相关资料时发现 bean 之间除了依赖关系还有其他一些关系。因此本章就对 bean 之间的关系进行整理,bean 之间的关系可以通过对 bean元素标签的设置起作用,完成一些特殊的功能。
在 Spring 容器中,两个 Bean 之间除了注入关系外,还存在继承、依赖和引用关系。
- 继承关系:在 Spring 容器当中允许使用 abstract 标签来定义一个父 bean,parent 标签来定义一个子 bean。子 bean 将自动继承父 bean 的配置信息。
- 依赖关系:Spring 允许用户通过 depends-on 标签来设定 bean 的前置依赖 bean,前置依赖的 bean 会在本 bean 实例化之前创建好,供本 bean 使用。
- 引用关系:不光可以通过 ref 标签来引用其他的 bean,而且可以通过 idref 标签来引用其他 bean 的名字。它的主要作用是:在 Spring 容器启动的时候就可以检查引用关系的正确性,从而可以提前发现配置信息是否存在错误。
继承
如果多个 bean 存在相同的配置信息,Spring 允许定义一个父 Bean,子 bean 将自动继承父 bean 的配置信息。Spring 会将父 bean 的配置信息传递个子 bean,如果子 bean 提供了父 bean 已有的配置信息,那么子 bean 的配置信息将覆盖父 bean 的配置信息。
新建一个 Car 类
public class Car { private int maxSpeed ; private double price ; private String brand ; private String color; public int getMaxSpeed() { return maxSpeed; } public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override public String toString() { return "Car{" + "maxSpeed=" + maxSpeed + ", price=" + price + ", brand='" + brand + '\'' + ", color='" + color + '\'' + '}'; } } 复制代码
然后在 beans.xml 文件中配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="car1" class="com.msdn.bean.Car" p:maxSpeed="240" p:price="5600" p:brand="宝马" p:color="红色" /> <bean id="car2" class="com.msdn.bean.Car" p:color="银色" parent="car1"/> </beans> 复制代码
上述代码中 car1 虽然被声明为父 bean,但是没有声明为 abstract=”true”,意味着父 bean 也会被实例化。如果仅仅是声明为抽象父类,不需要实例化,则可以定义 abstract="true" 。
测试代码
@Test public void inheritTest(){ ClassPathResource resource = new ClassPathResource("beans.xml"); BeanFactory beanFactory = new XmlBeanFactory(resource); Car car1 = (Car) beanFactory.getBean("car1"); System.out.println(car1); Car car2 = (Car) beanFactory.getBean("car2"); System.out.println(car2); } 复制代码
运行结果为:
Car{maxSpeed=240, price=5600.0, brand='宝马', color='红色'} Car{maxSpeed=240, price=5600.0, brand='宝马', color='银色'} 复制代码
从结果可以看出,car2 继承了 car1 的所有属性,只是改变了 color 属性,其他属性都跟 car1 相同。
依赖关系
在 Spring 容器中,当使用 depends-on 标签建立对其他 Bean 的依赖关系时,Spring 容器负责管理这些 Bean 的关系,当实例化一个 Bean 时,容器保证该 Bean 所依赖的 Bean 已经初始化;如果前置依赖多个 Bean,可以通过逗号或空格方式配置 Bean 名称。
depends-on 属性只是表明依赖关系(不一定会引用),这个依赖关系决定了被依赖的 bean 必定会在依赖 bean 之前被实例化,反过来,容器关闭时,依赖 bean 会在被依赖的 bean 之前被销毁。
新建一个 Person 类
public class Person { private String name; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", 拥有一辆car=" + car + '}'; } } 复制代码
在 beans.xml 中配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="car1" class="com.msdn.bean.Car" p:maxSpeed="240" p:price="5600" p:brand="宝马" p:color="红色" /> <!--<bean id="car2" class="com.msdn.bean.Car" p:color="银色" parent="car1"/>--> <bean id="person" class="com.msdn.bean.Person" p:name="herish" depends-on="car1" /> </beans> 复制代码
测试代码如下:
@Test public void relyTest(){ ClassPathResource resource = new ClassPathResource("beans.xml"); BeanFactory beanFactory = new XmlBeanFactory(resource); Car car1 = (Car) beanFactory.getBean("car1"); System.out.println(car1); Person person = (Person) beanFactory.getBean("person"); System.out.println(person); } 复制代码
执行结果为:
Car{maxSpeed=240, price=5600.0, brand='宝马', color='红色'} Person{name='herish', 拥有一辆car=null} 复制代码
从结果可以看出,car1 会先于 person 被实例化,而 person 不会将 car1 注入到自己的属性中。
引用
修改 Person 类
public class Person { private String name; private Car car; private String desc; //get set方法 @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", 拥有一辆car=" + car + '}'; } } 复制代码
新建 StringBean 类
public class StringBean { public StringBean() { System.out.println("当作String类"); } } 复制代码
修改 beans.xml 文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- <bean id="abstractCar" class="com.msdn.bean.AbstractCar" p:maxSpeed="240" p:price="56000" abstract="true"/>--> <bean id="car1" class="com.msdn.bean.Car" p:maxSpeed="240" p:price="5600" p:brand="宝马" p:color="红色" /> <!--<bean id="car2" class="com.msdn.bean.Car" p:color="银色" parent="car1"/>--> <bean id="stringBean" class="com.msdn.bean.StringBean" /> <bean id="person" class="com.msdn.bean.Person" p:name="herish" depends-on="car1" /> <bean id="person2" class="com.msdn.bean.Person" p:car-ref="car1"> <property name="name" value="hresh" /> <!--<property name="car"> <idref bean="car1" /> </property>--> <property name="desc"> <idref bean="stringBean" /> </property> </bean> </beans> 复制代码
测试代码如下
@Test public void refTest(){ ClassPathResource resource = new ClassPathResource("beans.xml"); BeanFactory beanFactory = new XmlBeanFactory(resource); Person person = (Person) beanFactory.getBean("person2"); System.out.println(person.getDesc()); System.out.println(person); } 复制代码
执行结果为:
stringBean Person{name='hresh', 拥有一辆car=Car{maxSpeed=240, price=5600.0, brand='宝马', color='红色'}} 复制代码
在测试过程中发现,标签引用 Car 的时候,运行结果会报错,错误信息如下:
nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.msdn.bean.Car' for property 'car': no matching editors or conversion strategy found 复制代码
StringBean 类并未被初始化,<idref>
的 bean 属性相当于 value 属性。