Spring 通过 IOC 容器的方式管理 Bean。在本篇,将通过 xml 配置的方式介绍如何配置 Bean。
1. 常见的 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"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<!-- 简单的 Bean 配置,后面详讲 -->
<bean id="animal" class="cn.cerish.entity.Animal"></bean>
</beans>
- xml 作为文件的开头,beans 是配置文件的顶级元素,不必讨论。
- xmlns(XML NameSpace):类似于一个保留字,它只用于声明命名空间。
- 若后面未使用前缀显式限定(如aop、context) ,则该元素将被隐式限定。即 声明了一个 默认命名空间,无论在任何时候都只能存在一个默认命名空间。
- 通常我们会用一个具有特点的名字来作为命名空间的前缀(例如这里的aop),在使用该命名空间的时候,需要加上该前缀,如
<aop:config>
<aop:aspect id="***" ref="***"/>
<aop:pointcut id="***" expression="****" />
</aop:config>
-
xsi(xml schema instance):schemaLocation 提供了一个xml 命名空间到对应的XSD(Xml Schema Definition)文件的一个映射,它的值由一个或多个URI引用对组成,两个URI之间以空白符分隔(空格和换行均可)。c p 命名空间没有对应的 xsd 文件,是在 handler 中处理的。
- 第一个URI是定义的 XML命名空间的值,
- 第二个URI给出Schema文档的实际位置,Schema处理器将从这个位置读取Schema文档,该文档的targetNamespace必须与第一个URI(XML命名空间的值)相匹配。
-
xmlns:c xmlns:p 分别指定 c、p命名空间,用于简化示例 Bean 的配置,后面会讲。
-
还有比较冷门的配置(xml属于 xml 文件的知识,xsi 属于 xsd 文件的知识)
- xml:base 重写默认URI
- xml:id 指定一个元素或属性有在文档范围内惟一的标识符
- xml:lang 设置 属性和内容的语言
- xml:space 设置应用程序如何处理空白,取值为default或preserve
- xsi:type 允许XML实例直接关联元素类型信息
- xsi:nil 允许在XSD可能不允许的情况下将空元素视为有效。
- xsi:noNamespaceSchemaLocation 没有命名空间的情况下使用,只给出 xsd 文件的URI
2. Bean 的详细配置
先定义几个类信息,作为 Bean 注入 ioc 容器
/* 省略 getter、setter 方法 */
public class Animal {
private Dog dog;
private Cat cat;
}
public class Dog {
private int age;
private String name;
}
public class Cat {
private int age;
private String name;
}
2.1 bean 与 alias
- 一个 Bean,可以使用 name 属性配置多个别名(使用逗号、空格、分号分割都可以),或者使用 alias 标签配置别名
- spring-cat.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="cat" name="cat1, cat2;cat5 cat6" class="cn.cerish.entity.Cat"></bean>
<alias name="cat" alias="cat3"></alias>
<alias name="cat1" alias="cat4"></alias>
</beans>
- 测试类
package cn.cerish;
import cn.cerish.entity.Cat;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IOCTest {
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-animal.xml");
Cat cat = (Cat) ac.getBean("cat");
Cat cat1 = (Cat) ac.getBean("cat1");
Cat cat2 = (Cat) ac.getBean("cat2");
Cat cat3 = (Cat) ac.getBean("cat3");
Cat cat4 = (Cat) ac.getBean("cat4");
Cat cat5 = (Cat) ac.getBean("cat5");
Cat cat6 = (Cat) ac.getBean("cat6");
System.out.println("cat: " + cat);
System.out.println("cat1: " + cat1);
System.out.println("cat2: " + cat2);
System.out.println("cat3: " + cat3);
System.out.println("cat4: " + cat4);
System.out.println("cat5: " + cat5);
System.out.println("cat6: " + cat6);
}
}
- 拿到的 Cat 对象都是同一个,这是因为 spring 默认 Bean 的生成是单实例。
2.2 import
- 在开发时,难免遇到需要使用多个配置文件的情况,可以使用 import 导入其他 xml 资源,进行整合。
- resourse 可以导入项目目录下的资源,也可以导入磁盘(file)、网络(http)的资源,这里只演示导入项目目录下的资源。
- spring-cat.xml 增加如下语句
<import resource="spring-dog.xml"></import>
- spring-dog.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="dog" class="cn.cerish.entity.Dog"></bean>
</beans>
- 测试类
package cn.cerish;
import cn.cerish.entity.*;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class IOCTest {
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-cat.xml");
Cat cat = (Cat) ac.getBean("cat");
System.out.println("cat: " + cat);
Dog dog = (Dog) ac.getBean("dog");
System.out.println("dog: " + dog);
}
}
- 可以看到,也是可以拿到 dog 对象的
2.3 description
- description 标签的作用在于,可以给当前配置文件作出描述,例如当前文件是为哪一模块进行配置的,且必须方法 beans 内的第一个标签,且只能配置一次。
<description>用来配置 Animal 信息的文件</description>
2.4 bean 的子节点详解
2.4.1 property
new ClassPathXmlApplicationContext("spring-animal.xml").getBean("XXX");
得到的 Bean 实例,默认经过空构造生成,属性值为默认的。如下图所示
- 可通过 property 配置属性默认值,本质上是调用 setter 方法赋值。
- 实体类
package cn.cerish.entity;
import java.util.*;
public class Student {
private int age;
private String name;
private Cat cat;
private String[] books;
private List<String> hobbys;
private Map<String, String> card;
private Set<String> games;
private String wife;
private Properties info;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobbys() {
return hobbys;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", cat=" + cat +
", books=" + Arrays.toString(books) +
", hobbys=" + hobbys +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
- spring-student.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<description>本配置文件用于展示 property 的属性配置</description>
<bean id="student" class="cn.cerish.entity.Student">
<property name="age" value="18"></property>
<property name="name" value="cerish"></property>
<property name="cat" ref="cat" />
<!-- 数组:注意这里要使用 list,使用 array会报错,不清楚为啥 -->
<property name="books">
<list>
<value>红楼梦</value>
<value>西游记</value>
<value>三国演义</value>
<value>水浒传</value>
</list>
</property>
<!-- List -->
<property name="hobbys">
<list>
<value>听歌</value>
<value>敲代码</value>
</list>
</property>
<!-- Map -->
<property name="card">
<map>
<entry key="idCard" value="4454644546"></entry>
</map>
</property>
<!-- set -->
<property name="games">
<set>
<value>Lol</value>
<value>王者荣耀</value>
</set>
</property>
<!-- Properties -->
<property name="info">
<props>
<prop key="学号">1520711</prop>
<prop key="性别">男</prop>
</props>
</property>
<!-- null值 -->
<property name="wife">
<null></null>
</property>
</bean>
<bean id="cat" class="cn.cerish.entity.Cat">
<property name="age" value="2" />
<property name="name" value="miao~" />
</bean>
</beans>
- 测试类
public class IOCTest {
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-student.xml");
Student student = (Student) ac.getBean("student");
System.out.println("student: " + student);
}
}
2.4.2 constructor-arg
- propety 是通过 setter 方法进行赋值,constructor-arg 则是通过构造方法进行赋值。
- constructor-arg 可通过三种方式进行赋值(当然,需要有对应的构造函数)
- index 属性,指定参数位置,例如
<constructor-arg index="0" value="1"></constructor-arg>
- type 属性,指定参数类型,例如
<constructor-arg type="java.lang.String" value="wang~"></constructor-arg>
- 前两者不推荐使用,建议使用 name 属性指定名称例如
<constructor-arg name="name" value="wang~"></constructor-arg>
- index 属性,指定参数位置,例如
2.5 bean 的属性详解
2.5.1 p、c命名空间
- 前面介绍了属性赋值与构造函数赋值,实际上 p、c命名空间就是对应用来简化赋值操作的。
- 若想使用 p、c命名空间,就需要先导入对应的配置,在 beans 标签中加入以下配置
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
- 完整的 spring-dog.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"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="dogC" class="cn.cerish.entity.Dog" c:age="3" c:name="wangC"></bean>
<bean id="dogP" class="cn.cerish.entity.Dog" p:age="2" p:name="wangP~"></bean>
</beans>
- 测试类
public class IOCTest {
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-dog.xml");
Dog dogC = (Dog) ac.getBean("dogC");
Dog dogP = (Dog) ac.getBean("dogP");
System.out.println("dogC: " + dogC);
System.out.println("dogP: " + dogP);
}
}
2.5.2 autowire
-
自动装配功能,从前面可以看到,若 Bean 对象的个数较多或者属性较多,那么需要配置很多个 property,spring 提供了 autowire 属性帮我们做了这件事。
-
autowire 的取值有五个
- no:不会自动注入。
- default:与 no 一致, 不会自动注入。
- byName:根据名字自动注入,本质上使用 setter 方法注入。
- byType:根据类型自动注入,本质上使用 setter 方法注入。
- constructor:根据构造器方法注入,需要有对应的构造器,本质上是根据构造函数的参数类型进行赋值,若有多个相同类型,则不赋值。(构造函数参数依次递减)
-
spring-dog.xml 配置如下,独自运行查看结果(例如,name 正确,类型不正确?类型多个?)
<?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
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!-- byName 实例,若名称相同,类型不同,会报错 Failed to convert property value of type xxx-->
<!--<bean id="animalName" class="cn.cerish.entity.Animal" autowire="byName"></bean>-->
<!--<bean id="cat" class="cn.cerish.entity.Cat">-->
<!-- <property name="age" value="1"></property>-->
<!-- <property name="name" value="miaoName~"></property>-->
<!--</bean>-->
<!--<bean id="dog" class="cn.cerish.entity.Dog">-->
<!-- <property name="age" value="1"></property>-->
<!-- <property name="name" value="wangName~"></property>-->
<!--</bean>-->
<!-- byType实例:根据类型查找Bean,若具有多个相同的类型,不进行匹配 -->
<bean id="animalType" class="cn.cerish.entity.Animal" autowire="byType"></bean>
<bean id="catType" class="cn.cerish.entity.Cat">
<property name="age" value="2"></property>
<property name="name" value="miaoType~"></property>
</bean>
<bean id="dogType" class="cn.cerish.entity.Dog">
<property name="age" value="2"></property>
<property name="name" value="wangType~"></property>
</bean>
<!-- 从参数最多的构造参数开始查找对应的类型,直至空构造为止 -->
<!--<bean id="animalConstructor" class="cn.cerish.entity.Animal" autowire="constructor"></bean>-->
<!--<bean id="catConstructor" class="cn.cerish.entity.Cat">-->
<!-- <property name="age" value="3"></property>-->
<!-- <property name="name" value="miaoConstructor~"></property>-->
<!--</bean>-->
<!--<bean id="dogConstructor" class="cn.cerish.entity.Dog">-->
<!-- <property name="age" value="3"></property>-->
<!-- <property name="name" value="wangConstructor~"></property>-->
<!--</bean>-->
</beans>
2.5.3 autowire-candidate
- autowire-candidate 是与 autowire 搭配使用的,每个在 xml 配置的 Bean,都默认为 自动注入的候选 Bean,使用autowire-candidate 可将指定的 Bean 从候选队列除名,值为 byName 时不生效
- spring-animal.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="animalType" class="cn.cerish.entity.Animal" autowire="byType"></bean>
<bean id="catType" class="cn.cerish.entity.Cat">
<property name="age" value="2"></property>
<property name="name" value="miao~"></property>
</bean>
<bean id="dogType" class="cn.cerish.entity.Dog" autowire-candidate="false">
<property name="age" value="2"></property>
<property name="name" value="wang~"></property>
</bean>
</beans>
- 测试类
public class IOCTest {
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-animal.xml");
Animal animal = (Animal) ac.getBean("animalType");
System.out.println("animal: " + animal);
}
}
2.5.4 scope
-
设置 Bean 的作用域
- singleton:单例模式,每次获取共享同一个实例
- prototype:多例模式,每次获取生成一个新的实例
- request:表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
- session:表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。
- golabl session:类似于标准的HTTP Session作用域,仅在基于portlet的web应用中才有意义。
- request、session、golabl session只有在 spring web 中使用才有效,这里不做过多讲解。
-
实体类
public class Person {
private int age;
private String name;
....省略 getter、setter
}
- spring-person.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="personSingleton" class="cn.cerish.entity.Person" scope="singleton"></bean>
<bean id="personPrototype" class="cn.cerish.entity.Person" scope="prototype"></bean>
</beans>
- 测试类
public class IOCTest {
@Test
public void test() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-person.xml");
Person person1 = (Person) ac.getBean("personSingleton");
Person person2 = (Person) ac.getBean("personSingleton");
Person person3 = (Person) ac.getBean("personSingleton");
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
System.out.println("person3: " + person3);
System.out.println("==============================");
Person person4 = (Person) ac.getBean("personPrototype");
Person person5 = (Person) ac.getBean("personPrototype");
Person person6 = (Person) ac.getBean("personPrototype");
System.out.println("person4: " + person4);
System.out.println("person5: " + person5);
System.out.println("person6: " + person6);
}
}
2.5.5 primary
- 在使用 autowire 自动注入的时候,会出现多个相同类型而导入的问题,可以使用 primary 将指定的 Bean 设置为首选的 Bean 解决,当然,如果设置了多个 primary 还是会报错。
- sprin-animal.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="animal" class="cn.cerish.entity.Animal" autowire="byType"></bean>
<bean id="cat" class="cn.cerish.entity.Cat">
<property name="age" value="2"></property>
<property name="name" value="miao~"></property>
</bean>
<bean id="dog1" class="cn.cerish.entity.Dog" primary="true">
<property name="age" value="2"></property>
<property name="name" value="wang~"></property>
</bean>
<bean id="dog2" class="cn.cerish.entity.Dog">
<property name="age" value="3"></property>
<property name="name" value="wang~"></property>
</bean>
<bean id="dog3" class="cn.cerish.entity.Dog">
<property name="age" value="4"></property>
<property name="name" value="wang~"></property>
</bean>
</beans>
- 测试类
@Test
public void testPrimary() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-animal.xml");
Animal animal = (Animal) ac.getBean("animal");
System.out.println("animal" + animal);
}
2.5.6 lazy-init
- 在 spring 中,所有的单实例 Bean 会在容器创建完成后进行实例化(多实例 Bean 不会提前创建),可将 lazy-init 赋值为 true,则通过 getBean 索取 bean 时才会实例化的。
- 实体类
/* Person 类 */
public class Person {
private int age;
private String name;
public Person() {
System.out.println("我是 person 空构造");
}
}
/* Animal 类 */
public class Animal {
private int age;
private String name;
public Animal () {
System.out.println("我是 Animal 空构造");
}
}
- sprin-person.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="personSingleton" class="cn.cerish.entity.Person" scope="singleton" lazy-init="true"></bean>
<bean id="animalSingleton" class="cn.cerish.entity.Animal" scope="singleton"></bean>
</beans>
- 测试类
@Test
public void testLazyInit() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-person.xml");
}
2.5.7 init-method & destroy-method
- 两个方法分别为 Bean 初始化时执行 与 销毁时执行的函数。
- 实体类
public class Person {
private int age;
private String name;
public Person() {
System.out.println("我是 person 空构造");
}
public void initMethod() {
System.out.println("我是 Person 类的 initMethod");
}
public void destoryMethod() {
System.out.println("我是 Person 类的 destoryMethod");
}
}
- spring-person.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="personInitDestory" class="cn.cerish.entity.Person" init-method="initMethod" destroy-method="destoryMethod"></bean>
</beans>
- 测试类
@Test
public void testInitDestory() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-person.xml");
Person person = (Person) ac.getBean("personInitDestory");
/* 关闭容器,意在销毁 Bean */
((ClassPathXmlApplicationContext) ac).close();
}
2.5.8 factory-method & factory-bean
- 之前的配置中,生成的 Bean 都是通过构造函数生成的,那么,如何指定函数去生产 Bean?factory-method 帮我们完成了这件事。
- 实体类
/* Car:实体类 */
public class Car {}
/* CarFactory: 生产 Car 的工厂类 */
public class CarFactory {
public static Car getCar() {
System.out.println("我是 static 方法创建的 Car");
return new Car();
}
public Car getInstanceCar() {
System.out.println("我是 static 方法创建的 Car");
return new Car();
}
}
- spring-car.xml 配置
<!-- lazy-init 是为了更好的区分静态方法与非静态方法 -->
<?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
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!-- 静态方法配置 -->
<bean id="staticCar" class="cn.cerish.entity.CarFactory" factory-method="getCar" lazy-init="true"></bean>
<!-- 非静态方法配置 -->
<bean id="carFactory" class="cn.cerish.entity.CarFactory" />
<bean id="instanceCar" factory-bean="carFactory" factory-method="getInstanceCar" lazy-init="true"></bean>
</beans>
- 测试类
@Test
public void testFactoryBean() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-car.xml");
// 静态方法获取对象
Car staticCar = (Car) ac.getBean("staticCar");
System.out.println("======================");
// 非静态方法获取对象
Car instanceCar = (Car) ac.getBean("instanceCar");
}
2.5.9 abstract & parent
- 类似于 Java 的继承,spring 的 Bean 也可以有继承。例如,Teacher 类拥有同一个学校,可以把 school 属性提取,作为公共部分被多个老师同时继承。
- 继承分为:类继承与公共部分继承,即 配置的 Bean 可以不是class类。
- 实体类
public class Teacher {
private String school;
private String name;
private int age;
....getter setter 方法
}
- spring-teacher.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<!-- 父类继承,若不想该类被容器加载,则设置 abstract="true" -->
<bean id="major" class="cn.cerish.entity.Major">
<property name="majorName" value="计算机技术"></property>
</bean>
<!-- 公共部分继承 -->
<bean name="schoolName" abstract="true">
<property name="school" value="清华大学"></property>
</bean>
<bean id="teacher1" parent="major" class="cn.cerish.entity.Teacher">
<property name="name" value="teacher1"></property>
<property name="age" value="28"></property>
<property name="school" value="北京大学"></property>
</bean>
<bean id="teacher2" parent="major" class="cn.cerish.entity.Teacher">
<property name="name" value="teacher2"></property>
<property name="age" value="38"></property>
<property name="school" value="北京大学"></property>
</bean>
<bean id="teacher3" parent="schoolName" class="cn.cerish.entity.Teacher">
<property name="name" value="teacher3"></property>
<property name="age" value="48"></property>
<property name="majorName" value="计算机技术"></property>
</bean>
<bean id="teacher4" parent="schoolName" class="cn.cerish.entity.Teacher">
<property name="name" value="teacher4"></property>
<property name="age" value="58"></property>
<property name="majorName" value="计算机技术"></property>
</bean>
</beans>
- 测试类
@Test
public void testAbstract() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-teacher.xml");
Teacher teacher1 = (Teacher) ac.getBean("teacher1");
System.out.println("teacher1: " + teacher1);
Teacher teacher2 = (Teacher) ac.getBean("teacher2");
System.out.println("teacher2: " + teacher2);
Teacher teacher3 = (Teacher) ac.getBean("teacher3");
System.out.println("teacher3: " + teacher3);
Teacher teacher4 = (Teacher) ac.getBean("teacher4");
System.out.println("teacher4: " + teacher4);
}
2.5.10 depends-on
- 在Bean之间没有明确的依赖关系时,depends-on 可以为 Bean 指定依赖关系。例如,需要先有 专业major 后有教师 teacher,则需要 Major 创建在前,销毁在后。
- 实体类
public class Teacher {
private String school;
private String name;
private int age;
}
public class Major {
private String majorName;
}
- spring-major.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="teacher" class="cn.cerish.entity.Teacher" init-method="initMethod" destroy-method="destoryMethod" depends-on="major">
<property name="name" value="teacher"></property>
<property name="age" value="28"></property>
</bean>
<bean id="major" class="cn.cerish.entity.Major" init-method="initMethod" destroy-method="destoryMethod">
<property name="majorName" value="计算机科学"></property>
</bean>
</beans>
- 测试类
@Test
public void testDependOn() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-major.xml");
((ClassPathXmlApplicationContext) ac).close();
}
3. 奇思妙想计
3.1 若使用了相同的 id 配置不同的Bean,会发生什么?配置相同的Bean 呢?
- 相同的id,不同的类型,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"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="dog" class="cn.cerish.entity.Dog">
<property name="name" value="wang~~"></property>
<property name="age" value="2"></property>
</bean>
<bean id="dog" class="cn.cerish.entity.Cat">
<property name="name" value="miao~~"></property>
<property name="age" value="1"></property>
</bean>
</beans>
- 结果报错如下:(相同的类型也报错)
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Bean name 'dog' is already used in this <beans> element
Offending resource: class path resource [spring-dog.xml]
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:72)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:119)...
3.2 property 与 命名空间同时配置属性,哪个优先级高?
- spring-dog.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"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="dog" class="cn.cerish.entity.Dog" p:name="wangP:~" p:age="1">
<property name="name" value="wangProperty~"></property>
<property name="age" value="2"></property>
</bean>
</beans>
- 测试类
@Test
public void testPriority() {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-dog.xml");
Dog dog = (Dog) ac.getBean("dog");
System.out.println(dog);
}
- 结果为,报错 BeanDefinitionParsingException
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Property 'age' is already defined using both <property> and inline syntax. Only one approach may be used per property.
Offending resource: class path resource [spring-dog.xml]
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:72)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:119)...