一、Bean 的装配
Bean 的装配可以理解为依赖关系注入,Bean 的装配方式也就是 Bean 的依赖注入方式。Spring容器支持多种形式的 Bean 的装配方式,如基于 XML 的 Bean 装配、基于 Annotation 的 Bean 装配和自动装配等。
Spring 基于 XML 的装配通常采用两种实现方式,即set注入和构造注入。
1、set注入
在 Spring 实例化 Bean 的过程中,首先会调用默认的构造方法实例化 Bean 对象,然后通过Java的反射机制调用 setXxx() 方法进行属性的注入。因此,设值注入要求一个 Bean 的对应类必须满足以下两点要求:
- 必须提供一个默认的无参构造方法。
- 必须为需要注入的属性提供对应的 setter 方法。
接下来就用一个简单案例演示一下set注入。步骤如下:
(1)创建Person类,并提供私有属性name和age以及它们的set方法,覆写toString()。
public class Person {
private String name;
private Integer age;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(2)在bean.xml文件中配置person对象,用set注入方式注入属性值。
<!-- 配置Person -->
<bean id="person" class="com.yht.example1.entity.Person">
<!-- name:类中属性的名称 value:向属性注入的值 -->
<property name="name" value="张三"></property>
<property name="age" value="33"></property>
</bean>
(3)进行单元测试
@Test
public void testPerson(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
执行结果如下:
2、构造注入
使用设值注入时,在 Spring 配置文件中,需要使用 <bean> 元素的子元素 <property> 元素为每个属性注入值。而使用构造注入时,在配置文件中,主要使用 <constructor-arg> 标签定义构造方法的参数,可以使用其 value 属性(或子元素)设置该参数的值。下面通过案例演示基于 XML 方式的 Bean 的装配。
构造注入有两种方式:第一种通过 <constructor-arg>的子元素name完成;第二种通过 <constructor-arg>的子元素index来完成,这里的index指的是属性在构造方法中的下标,从0开始。来做一个简单测试:
(1)创建Book类,提供私有属性bName和bAuthor,并提供有参构造(bName和bAuthor)。
public class Book {
private String bName;
private String bAuthor;
public Book(String bName, String bAuthor) {
this.bName = bName;
this.bAuthor = bAuthor;
}
@Override
public String toString() {
return "Book{" +
"bName='" + bName + '\'' +
", bAuthor='" + bAuthor + '\'' +
'}';
}
}
(2)在bean.xml中配置person对象,用构造注入的方式注入属性值。
<!-- 配置book:用name的方式 -->
<bean id="book1" class="com.yht.example1.entity.Book">
<constructor-arg name="bName" value="三国演义"></constructor-arg>
<constructor-arg name="bAuthor" value="罗贯中"></constructor-arg>
</bean>
<!-- 配置book:用index的方式 -->
<bean id="book2" class="com.yht.example1.entity.Book">
<constructor-arg name="bName" value="水浒传"></constructor-arg>
<constructor-arg name="bAuthor" value="施耐庵"></constructor-arg>
</bean>
(3)进行单元测试。
@Test
public void testBook(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//获取用name注入的对象
Book book1 = (Book) applicationContext.getBean("book1");
System.out.println(book1);
//获取用index注入的对象
Book book2 = (Book) applicationContext.getBean("book2");
System.out.println(book2);
}
执行结果如下:
3、p名称空间注入
从 2.0开始,Spring支持使用名称空间的可扩展配置格式。这些名称空间都是基于一种XML Schema定义。事实上,我们所看到的所有bean的配置格式都是基于一个 XML Schema文档。
p名称空间并不需要定义在一个XSD文件中,它只在Spring内核中存在采用p名称空间就可以在bean元素中使用属性来描述property的值。
写法:
普通属性 p:属性名=”值”
对象属性 p:属性名-ref=”值”
注意事项:
P名称空间注入走的也是set方法,官方目的是简化set注入的property标签的写法,所以并不适用于构造注入。
我们以注入Person对象的属性值为例,步骤如下:
(1)在bean.xml中导入p名称空间。
xmlns:p="http://www.springframework.org/schema/p"
(2)配置person对象,注入属性。
<!-- p名称空间注入 -->
<bean id="person2" class="com.yht.example1.entity.Person" p:name="李四" p:age="24" >
</bean>
(3)进行单元测试。
@Test
public void testPerson(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
//set注入
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
//p名称空间注入
Person person2 = (Person) applicationContext.getBean("person2");
System.out.println(person2);
}
执行结果如下:
二、Bean装配的几点说明
1、bean的子元素name
bean除了id和class属性,还有一个name属性,Spring 容器可以通过此属性对容器中的 Bean 进行配置和管理,name 属性中可以为 Bean 指定多个名称,每个名称之间用逗号或分号隔开,且可以包含特殊字符。我们用案例来验证一下:
(1)在bean.xml配置person对象,并给定多个值。
<!-- 测试bean的name属性 -->
<bean class="com.yht.example1.entity.Person" name="p1,p2,p3@">
<property name="name" value="赵飞"></property>
<property name="age" value="23"></property>
</bean>
(2)进行单元测试。
@Test
public void testPersonP(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
Person p1 = (Person) applicationContext.getBean("p1");
System.out.println(p1);
Person p2 = (Person) applicationContext.getBean("p2");
System.out.println(p2);
Person p3 = (Person) applicationContext.getBean("p3@");//含有特殊字符
System.out.println(p3);
}
执行结果如下:
注意点:不管是id还是name,其值都不能重复。即:id之间、name之间、id和name之间都必须唯一。如下:
2、set注入和构造注入是否可以混合使用
(1)对Person类做一些修改,添加无参构造、含有name的有参构造。
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(2)在bean.xml中配置Person对象,注入属性
<!--set注入和构造注入混合使用-->
<bean id="per" class="com.yht.example1.entity.Person">
<constructor-arg name="name" value="tom"></constructor-arg>
<property name="age" value="41"></property>
</bean>
(3)进行单元测试。
@Test
public void testPer(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
Person per = (Person) applicationContext.getBean("per");
System.out.println(per);
}
执行结果如下:
结论:set注入和构造注入可以同时使用,但不推荐。