Spring Bean属性注入
所谓 Bean 属性注入,简单点说就是将属性注入到 Bean 中的过程,而这属性既可以普通属性,也可以是一个对象(Bean)。
Spring 主要通过以下 2 种方式实现属性注入:
使用构造函数实现属性注入大致步骤如下:
1. 新建一个名为 my-spring-demo 的 Java 项目,并在 src 下创建一个名为 net.biancheng.c 的包。
2. 参考《第一个Spring程序》,向项目中导入所需的 Jar 包。
3. 在 net.biancheng.c 包下,创建一个名为 Grade 的类,代码如下。
4. 在 net.biancheng.c 包下,创建一个名为 Student 的类,代码如下。
5. 在 src 目录下创建 Spring 配置文件 Beans.xml,配置如下。
6. 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。
7. 执行 MainApp 中的 main() 方法,控制台输出如下。
在 Spring 实例化 Bean 的过程中,IoC 容器首先会调用默认的构造方法(无参构造方法)实例化 Bean(Java 对象),然后通过 Java 的反射机制调用这个 Bean 的 setXxx() 方法,将属性值注入到 Bean 中。
使用 setter 注入的方式进行属性注入,大致步骤如下:
1. 在 net.biancheng.c 包下,修改 Student 类的代码。
4. 在 net.biancheng.c 包下,修改 Grade 类的代码。
4. 在 src 目录下,修改配置文件 Beans.xml 的内容。
5. 执行 MainApp 中的 main() 方法,控制台输出如下。
Spring 框架提供了 2 种短命名空间,可以简化 Spring 的 XML 配置,如下表。
首先我们需要在配置文件的 <beans> 元素中导入以下 XML 约束。
在导入 XML 约束后,我们就能通过以下形式实现属性注入。
使用 p 命名空间注入依赖时,必须注意以下 3 点:
1. 参考《第一个 Spring程序》,新建一个名为 my-spring-demo6 的 Java 项目。
2. 在 net.biancheng.c 包中,创建一个名为 Dept 的类,代码如下。
3. 在 net.biancheng.c 包下,创建一个名为 Employee 的类,代码如下。
4. 在 src 目录下创建 Spring 配置文件 Beans.xml,配置如下。
5. 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。
6. 执行 MainApp 类中的 main() 方法,控制台输出如下。
首先我们需要在配置文件的 <beans> 元素中导入以下 XML 约束。
在导入 XML 约束后,我们就能通过以下形式实现属性注入。
使用 c 命名空间注入依赖时,必须注意以下 2 点:
1. 修改 Dept 中的代码,添加一个有参构造函数。
2. 修改 Employee 中的代码,添加一个有参构造函数。
3. 修改 Beans.xml 中的配置,使用 c 命名空间实现属性注入。
4. 重新执行 MainApp 中的 main() 方法,控制台输出如下。
Spring 主要通过以下 2 种方式实现属性注入:
- 构造函数注入
- setter 注入(又称设值注入)
构造函数注入
我们可以通过 Bean 的带参构造函数,以实现 Bean 的属性注入。使用构造函数实现属性注入大致步骤如下:
- 在 Bean 中添加一个有参构造函数,构造函数内的每一个参数代表一个需要注入的属性;
- 在 Spring 的 XML 配置文件中,通过 <beans> 及其子元素 <bean> 对 Bean 进行定义;
- 在 <bean> 元素内使用 <constructor-arg> 元素,对构造函数内的属性进行赋值,Bean 的构造函数内有多少参数,就需要使用多少个 <constructor-arg> 元素。
示例 1
下面我们就通过一个实例,来演示下如何构造函数注入的方式实现属性注入。1. 新建一个名为 my-spring-demo 的 Java 项目,并在 src 下创建一个名为 net.biancheng.c 的包。
2. 参考《第一个Spring程序》,向项目中导入所需的 Jar 包。
3. 在 net.biancheng.c 包下,创建一个名为 Grade 的类,代码如下。
package net.biancheng.c; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Grade { private static final Log LOGGER = LogFactory.getLog(Grade.class); private Integer gradeId; private String gradeName; public Grade(Integer gradeId, String gradeName) { LOGGER.info("正在执行 Course 的有参构造方法,参数分别为:gradeId=" + gradeId + ",gradeName=" + gradeName); this.gradeId = gradeId; this.gradeName = gradeName; } @Override public String toString() { return "Grade{" + "gradeId=" + gradeId + ", gradeName='" + gradeName + '\'' + '}'; } }
4. 在 net.biancheng.c 包下,创建一个名为 Student 的类,代码如下。
package net.biancheng.c; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Student { private static final Log LOGGER = LogFactory.getLog(Student.class); private int id; private String name; private Grade grade; public Student(int id, String name, Grade grade) { LOGGER.info("正在执行 Course 的有参构造方法,参数分别为:id=" + id + ",name=" + name + ",grade=" + grade); this.id = id; this.name = name; this.grade = grade; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", grade=" + grade + '}'; } }
5. 在 src 目录下创建 Spring 配置文件 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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="student" class="net.biancheng.c.Student"> <constructor-arg name="id" value="2"></constructor-arg> <constructor-arg name="name" value="李四"></constructor-arg> <constructor-arg name="grade" ref="grade"></constructor-arg> </bean> <bean id="grade" class="net.biancheng.c.Grade"> <constructor-arg name="gradeId" value="4"></constructor-arg> <constructor-arg name="gradeName" value="四年级"></constructor-arg> </bean> </beans>
6. 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。
package net.biancheng.c; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { private static final Log LOGGER = LogFactory.getLog(MainApp.class); public static void main(String[] args) { //获取 ApplicationContext 容器 ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); //获取名为 student 的 Bean Student student = context.getBean("student", Student.class); //通过日志打印学生信息 LOGGER.info(student.toString()); } }
7. 执行 MainApp 中的 main() 方法,控制台输出如下。
十二月 16, 2021 4:38:42 下午 net.biancheng.c.Grade <init> 信息: 正在执行 Course 的有参构造方法,参数分别为:gradeId=4,gradeName=四年级 十二月 16, 2021 4:38:42 下午 net.biancheng.c.Student <init> 信息: 正在执行 Course 的有参构造方法,参数分别为:id=2,name=李四,grade=Grade{gradeId=4, gradeName='四年级'} 十二月 16, 2021 4:38:42 下午 net.biancheng.c.MainApp main 信息: Student{id=2, name='李四', grade=Grade{gradeId=4, gradeName='四年级'}}
setter 注入
我们可以通过 Bean 的 setter 方法,将属性值注入到 Bean 的属性中。在 Spring 实例化 Bean 的过程中,IoC 容器首先会调用默认的构造方法(无参构造方法)实例化 Bean(Java 对象),然后通过 Java 的反射机制调用这个 Bean 的 setXxx() 方法,将属性值注入到 Bean 中。
使用 setter 注入的方式进行属性注入,大致步骤如下:
- 在 Bean 中提供一个默认的无参构造函数(在没有其他带参构造函数的情况下,可省略),并为所有需要注入的属性提供一个 setXxx() 方法;
- 在 Spring 的 XML 配置文件中,使用 <beans> 及其子元素 <bean> 对 Bean 进行定义;
- 在 <bean> 元素内使用 <property> 元素对各个属性进行赋值。
示例 2
下面,我们就通过一个实例,来演示如何通过 setter 注入的方式实现属性注入,步骤如下。1. 在 net.biancheng.c 包下,修改 Student 类的代码。
package net.biancheng.c; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Student { private static final Log LOGGER = LogFactory.getLog(Student.class); private int id; private String name; private Grade grade; //无参构造方法,在没有其他带参构造方法的情况下,可以省略 public Student() { } //id 属性的 setter 方法 public void setId(int id) { LOGGER.info("正在执行 Student 类的 setId() 方法…… "); this.id = id; } //name 属性的 setter 方法 public void setName(String name) { LOGGER.info("正在执行 Student 类的 setName() 方法…… "); this.name = name; } public void setGrade(Grade grade) { LOGGER.info("正在执行 Student 类的 setGrade() 方法…… "); this.grade = grade; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", grade=" + grade + '}'; } }
4. 在 net.biancheng.c 包下,修改 Grade 类的代码。
package net.biancheng.c; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class Grade { private static final Log LOGGER = LogFactory.getLog(Grade.class); private Integer gradeId; private String gradeName; /** * 无参构造函数 * 若该类中不存在其他的带参构造函数,则这个默认的无参构造函数可以省略 */ public Grade() { } public void setGradeId(Integer gradeId) { LOGGER.info("正在执行 Grade 类的 setGradeId() 方法…… "); this.gradeId = gradeId; } public void setGradeName(String gradeName) { LOGGER.info("正在执行 Grade 类的 setGradeName() 方法…… "); this.gradeName = gradeName; } @Override public String toString() { return "Grade{" + "gradeId=" + gradeId + ", gradeName='" + gradeName + '\'' + '}'; } }
4. 在 src 目录下,修改配置文件 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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="student" class="net.biancheng.c.Student"> <!--使用 property 元素完成属性注入 name: 类中的属性名称,例如 id,name value: 向属性注入的值 例如 学生的 id 为 1,name 为张三 --> <property name="id" value="1"></property> <property name="name" value="张三"></property> <property name="grade" ref="grade"></property> </bean> <bean id="grade" class="net.biancheng.c.Grade"> <property name="gradeId" value="3"></property> <property name="gradeName" value="三年级"></property> </bean> </beans>
5. 执行 MainApp 中的 main() 方法,控制台输出如下。
十二月 16, 2021 4:01:13 下午 net.biancheng.c.Grade setGradeId 信息: 正在执行 Grade 类的 setGradeId() 方法…… 十二月 16, 2021 4:01:13 下午 net.biancheng.c.Grade setGradeName 信息: 正在执行 Grade 类的 setGradeName() 方法…… 十二月 16, 2021 4:01:13 下午 net.biancheng.c.Student setId 信息: 正在执行 Student 类的 setId() 方法…… 十二月 16, 2021 4:01:13 下午 net.biancheng.c.Student setName 信息: 正在执行 Student 类的 setName() 方法…… 十二月 16, 2021 4:01:13 下午 net.biancheng.c.Student setGrade 信息: 正在执行 Student 类的 setGrade() 方法…… 十二月 16, 2021 4:01:13 下午 net.biancheng.c.MainApp main 信息: Student{id=1, name='张三', grade=Grade{gradeId=3, gradeName='三年级'}}
短命名空间注入
我们在通过构造函数或 setter 方法进行属性注入时,通常是在 <bean> 元素中嵌套 <property> 和 <constructor-arg> 元素来实现的。这种方式虽然结构清晰,但书写较繁琐。Spring 框架提供了 2 种短命名空间,可以简化 Spring 的 XML 配置,如下表。
短命名空间 | 简化的 XML 配置 | 说明 |
---|---|---|
p 命名空间 | <bean> 元素中嵌套的 <property> 元素 | 是 setter 方式属性注入的一种快捷实现方式 |
c 命名空间 | <bean> 元素中嵌套的 <constructor> 元素 | 是构造函数属性注入的一种快捷实现方式 |
p 命名空间注入
p 命名空间是 setter 方式属性注入的一种快捷实现方式。通过它,我们能够以 bean 属性的形式实现 setter 方式的属性注入,而不再使用嵌套的 <property> 元素,以实现简化 Spring 的 XML 配置的目的。首先我们需要在配置文件的 <beans> 元素中导入以下 XML 约束。
xmlns:p="http://www.springframework.org/schema/p"
在导入 XML 约束后,我们就能通过以下形式实现属性注入。
<bean id="Bean 唯一标志符" class="包名+类名" p:普通属性="普通属性值" p:对象属性-ref="对象的引用">
使用 p 命名空间注入依赖时,必须注意以下 3 点:
- Java 类中必须有 setter 方法;
- Java 类中必须有无参构造器(类中不包含任何带参构造函数的情况,无参构造函数默认存在);
- 在使用 p 命名空间实现属性注入前,XML 配置的 <beans> 元素内必须先导入 p 命名空间的 XML 约束。
示例
下面我们通过一个简单的实例,演示下如何通过 p 命名空间实现属性注入。1. 参考《第一个 Spring程序》,新建一个名为 my-spring-demo6 的 Java 项目。
2. 在 net.biancheng.c 包中,创建一个名为 Dept 的类,代码如下。
package net.biancheng.c; public class Dept { //部门编号 private String deptNo; //部门名称 private String deptName; public void setDeptNo(String deptNo) { this.deptNo = deptNo; } public void setDeptName(String deptName) { this.deptName = deptName; } @Override public String toString() { return "Dept{" + "deptNo='" + deptNo + '\'' + ", deptName='" + deptName + '\'' + '}'; } }
3. 在 net.biancheng.c 包下,创建一个名为 Employee 的类,代码如下。
package net.biancheng.c; public class Employee { //员工编号 private String empNo; //员工姓名 private String empName; //部门信息 private Dept dept; public void setEmpNo(String empNo) { this.empNo = empNo; } public void setEmpName(String empName) { this.empName = empName; } public void setDept(Dept dept) { this.dept = dept; } public Dept getDept() { return dept; } @Override public String toString() { return "Employee{" + "empNo='" + empNo + '\'' + ", empName='" + empName + '\'' + ", dept=" + dept + '}'; } }
4. 在 src 目录下创建 Spring 配置文件 Beans.xml,配置如下。
<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-3.0.xsd"> <bean id="employee" class="net.biancheng.c.Employee" p:empName="小李" p:dept-ref="dept" p:empNo="22222"></bean> <bean id="dept" class="net.biancheng.c.Dept" p:deptNo="1111" p:deptName="技术部"></bean> </beans>
5. 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。
package net.biancheng.c; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { private static final Log LOGGER = LogFactory.getLog(MainApp.class); public static void main(String[] args) { //获取 ApplicationContext 容器 ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml"); //获取名为 employee 的 Bean Employee employee = context.getBean("employee", Employee.class); //通过日志打印信息 LOGGER.info(employee.toString()); } }
6. 执行 MainApp 类中的 main() 方法,控制台输出如下。
十二月 20, 2021 3:18:54 下午 net.biancheng.c.MainApp main 信息: Employee{empNo='22222', empName='小李', dept=Dept{deptNo='1111', deptName='技术部'}}
c 命名空间注入
c 命名空间是构造函数注入的一种快捷实现方式。通过它,我们能够以 <bean> 属性的形式实现构造函数方式的属性注入,而不再使用嵌套的 <constructor-arg> 元素,以实现简化 Spring 的 XML 配置的目的。首先我们需要在配置文件的 <beans> 元素中导入以下 XML 约束。
xmlns:c="http://www.springframework.org/schema/c"
在导入 XML 约束后,我们就能通过以下形式实现属性注入。
<bean id="Bean 唯一标志符" class="包名+类名" c:普通属性="普通属性值" c:对象属性-ref="对象的引用">
使用 c 命名空间注入依赖时,必须注意以下 2 点:
- Java 类中必须包含对应的带参构造器;
- 在使用 c 命名空间实现属性注入前,XML 配置的 <beans> 元素内必须先导入 c 命名空间的 XML 约束。
示例
下面我们通过一个简单的实例,演示下如何通过 c 命名空间实现属性注入。1. 修改 Dept 中的代码,添加一个有参构造函数。
package net.biancheng.c; public class Dept { //部门编号 private String deptNo; //部门名称 private String deptName; public Dept(String deptNo, String deptName) { this.deptNo = deptNo; this.deptName = deptName; } @Override public String toString() { return "Dept{" + "deptNo='" + deptNo + '\'' + ", deptName='" + deptName + '\'' + '}'; } }
2. 修改 Employee 中的代码,添加一个有参构造函数。
package net.biancheng.c; public class Employee { //员工编号 private String empNo; //员工姓名 private String empName; //部门信息 private Dept dept; public Employee(String empNo, String empName, Dept dept) { this.empNo = empNo; this.empName = empName; this.dept = dept; } @Override public String toString() { return "Employee{" + "empNo='" + empNo + '\'' + ", empName='" + empName + '\'' + ", dept=" + dept + '}'; } }
3. 修改 Beans.xml 中的配置,使用 c 命名空间实现属性注入。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="employee" class="net.biancheng.c.Employee" c:empName="小胡" c:dept-ref="dept" c:empNo="999"></bean> <bean id="dept" class="net.biancheng.c.Dept" c:deptNo="2222" c:deptName="测试部"></bean> </beans>
4. 重新执行 MainApp 中的 main() 方法,控制台输出如下。
十二月 20, 2021 3:35:33 下午 net.biancheng.c.MainApp main 信息: Employee{empNo='999', empName='小胡', dept=Dept{deptNo='2222', deptName='测试部'}}