7.1 Spring Bean 注入属性
所谓 Spring Bean 注入属性,简单点说就是将属性注入到 Bean 中的过程,而这属性既可以普通属性,也可以是一个对象(内部 Bean)。
Spring 主要通过以下 2 种方式实现注入属性:
- 构造函数注入
- setter 注入(又称设置注入)
1 构造函数注入
我们可以通过 Bean 的带参构造函数,以实现 Bean 的属性注入。
使用构造函数实现属性注入大致步骤如下:
1)在 Bean 中添加一个有参构造函数,构造函数内的每一个参数代表一个需要注入的属性;
2)在 Spring 的 XML 配置文件中,通过 <beans>
及其子元素 <bean>
对 Bean 进行定义;
3)在 <bean>
元素内使用 <constructor-arg>
元素,对构造函数内的属性进行赋值,Bean 的构造函数内有多少参数,就需要使用多少个 <constructor-arg>
例 1
下面我们就通过一个实例,来演示下如何构造函数注入的方式实现属性注入。
创建命名为 Grade 的类,代码如下:
package section1.demo1;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* ClassName: Grade
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class Grade {
private static final Log LOGGER = LogFactory.getLog(Grade.class);
// 年级ID
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 + '\'' +
'}';
}
}
创建命名为 Student 的类,代码如下:
package section1.demo1;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* ClassName: Student
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class Student {
private static final Log LOGGER = LogFactory.getLog(Student.class);
// 学生ID
private Integer id;
// 学生姓名
private String name;
// 学生所属班级
private Grade grade;
public Student(Integer 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 +
'}';
}
}
修改 Spring 配置文件 spring-config.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.xsd">
<bean id="grade" class="section1.demo1.Grade">
<constructor-arg name="gradeId" value="1"/>
<constructor-arg name="gradeName" value="一年级"/>
</bean>
<bean id="student" class="section1.demo1.Student">
<constructor-arg name="id" value="9527"/>
<constructor-arg name="name" value="传陆"/>
<constructor-arg name="grade" ref="grade"/>
</bean>
</beans>
创建命名为 MainApp 的类,代码如下:
package section1.demo1;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* ClassName: MainApp
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class MainApp {
private static final Log LOGGER = LogFactory.getLog(MainApp.class);
public static void main(String[] args) {
// 获取 ApplicationContext 容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 获取名为 student 的 Bean
Student student = context.getBean("student", Student.class);
// 通过日志打印学生信息
LOGGER.info(student.toString());
}
}
执行 MainApp 中的 main 方法,控制台输出如下:
4月 08, 2022 11:10:56 上午 section1.demo1.Grade <init>
信息: 正在执行 Course 的有参构造方法,参数分别为:gradeId=1,gradeName=一年级
4月 08, 2022 11:10:56 上午 section1.demo1.Student <init>
信息: 正在执行 Course 的有参构造方法,参数分别为:id=9527,name=传陆,grade=Grade{gradeId=1, gradeName='一年级'}
4月 08, 2022 11:10:56 上午 section1.demo1.MainApp main
信息: Student{id=9527, name='传陆', grade=Grade{gradeId=1, gradeName='一年级'}}
2. setter 方法注入
我们可以通过 Bean 的 setter 方法,将属性值注入到 Bean 的属性中。
在 Spring 实例化 Bean 的过程中,IoC 容器首先会调用默认的构造方法(无参构造方法)实例化 Bean(Java 对象),然后通过 Java 的反射机制调用这个 Bean 的 setXxx() 方法,将属性值注入到 Bean 中。
使用 setter 注入的方式进行属性注入,大致步骤如下:
1)在 Bean 中提供一个默认的无参构造函数(在没有其他带参构造函数的情况下,可省略),并为所有需要注入的属性提供一个 setXxx() 方法;
2)在 Spring 的 XML 配置文件中,使用 <beans>
及其子元素 <bean>
对 Bean 进行定义;
3)在 <bean>
元素内使用 <property>
例 2
下面,我们就通过一个实例,来演示如何通过 setter 注入的方式实现属性注入。
创建命名为 Grade 的类,代码如下:
package section1.demo2;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* ClassName: Grade
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class Grade {
private static final Log LOGGER = LogFactory.getLog(Grade.class);
// 年级ID
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 + '\'' +
'}';
}
}
创建命名为 Student 的类,代码如下:
package section1.demo2;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* ClassName: Student
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class Student {
private static final Log LOGGER = LogFactory.getLog(Student.class);
// 学生ID
private Integer id;
// 学生姓名
private String name;
// 学生所属班级
private Grade grade;
// 无参构造方法,在没有其他带参构造方法的情况下,可以省略
public Student() {
}
public void setId(Integer id) {
LOGGER.info("正在执行 Student 类的 setId() 方法…… ");
this.id = id;
}
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 +
'}';
}
}
修改 Spring 配置文件 spring-config.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.xsd">
<bean id="grade" class="section1.demo2.Grade">
<property name="gradeId" value="1"/>
<property name="gradeName" value="一年级"/>
</bean>
<bean id="student" class="section1.demo2.Student">
<property name="id" value="9527"/>
<property name="name" value="传陆"/>
<property name="grade" ref="grade"/>
</bean>
</beans>
创建命名为 MainApp 的类,代码如下:
package section1.demo2;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* ClassName: MainApp
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class MainApp {
private static final Log LOGGER = LogFactory.getLog(MainApp.class);
public static void main(String[] args) {
// 获取 ApplicationContext 容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 获取名为 student 的 Bean
Student student = context.getBean("student", Student.class);
// 通过日志打印学生信息
LOGGER.info(student.toString());
}
}
执行 MainApp 中的 main 方法,控制台输出如下:
4月 08, 2022 11:15:32 上午 section1.demo2.Grade setGradeId
信息: 正在执行 Grade 类的 setGradeId() 方法……
4月 08, 2022 11:15:32 上午 section1.demo2.Grade setGradeName
信息: 正在执行 Grade 类的 setGradeName() 方法……
4月 08, 2022 11:15:32 上午 section1.demo2.Student setId
信息: 正在执行 Student 类的 setId() 方法……
4月 08, 2022 11:15:32 上午 section1.demo2.Student setName
信息: 正在执行 Student 类的 setName() 方法……
4月 08, 2022 11:15:32 上午 section1.demo2.Student setGrade
信息: 正在执行 Student 类的 setGrade() 方法……
4月 08, 2022 11:15:32 上午 section1.demo2.MainApp main
信息: Student{id=9527, name='传陆', grade=Grade{gradeId=1, gradeName='一年级'}}
3. 短命名空间注入
我们在通过构造函数或 setter 方法进行属性注入时,通常是在 <bean>
元素中嵌套 <property>
和 <constructor-arg>
Spring 框架提供了 2 种短命名空间,可以简化 Spring 的 XML 配置,如下表所示:
短命名空间 | 简化的 XML 配置 | 说明 |
p 命名空间 | <bean> 元素中嵌套的 <property> 元素 | 是 setter 方式属性注入的一种快捷实现方式 |
c 命名空间 | <bean> 元素中嵌套的 <constructor-arg> 元素 | 是构造函数属性注入的一种快捷实现方式 |
p 命名空间注入
p 命名空间是 setter 方式属性注入的一种快捷实现方式。通过它,我们能够以 bean 属性的形式实现 setter 方式的属性注入,而不再使用嵌套的 <property>
首先我们需要在配置文件的 <beans>
xmlns:p="http://www.springframework.org/schema/p"
在导入 XML 约束后,我们就能通过以下形式实现属性注入。
<bean id="Bean 唯一标志符" class="包名+类名" p:普通属性="普通属性值" p:对象属性-ref="对象的引用">
使用 p 命名空间注入依赖时,必须注意以下 3 点:
- Java 类中必须有 setter 方法;
- Java 类中必须有无参构造器(类中不包含任何带参构造函数的情况,无参构造函数默认存在);
- 在使用 p 命名空间实现属性注入前,XML 配置的
<beans>
例 3
下面我们通过一个简单的实例,演示下如何通过 p 命名空间实现属性注入。
创建命名为 Department 的类,代码如下:
package section1.demo3;
/**
* ClassName: Department
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class Department {
// 部门编号
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 "Department{" +
"deptNo='" + deptNo + '\'' +
", deptName='" + deptName + '\'' +
'}';
}
}
创建命名为 Employee 的类,代码如下:
package section1.demo3;
/**
* ClassName: Student
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class Employee {
// 员工编号
private String empNo;
// 员工姓名
private String empName;
// 员工所属部门
private Department dept;
public String getEmpNo() {
return empNo;
}
public void setEmpNo(String empNo) {
this.empNo = empNo;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Employee{" +
"empNo='" + empNo + '\'' +
", empName='" + empName + '\'' +
", dept=" + dept +
'}';
}
}
修改 Spring 配置文件 spring-config.xml,使用 p 命名空间实现属性注入,代码如下:
<?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="dept" class="section1.demo3.Department" p:deptNo="007" p:deptName="研发部"/>
<bean id="employee" class="section1.demo3.Employee" p:empName="传陆" p:dept-ref="dept" p:empNo="9527"/>
</beans>
创建命名为 MainApp 的类,代码如下:
package section1.demo3;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* ClassName: MainApp
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class MainApp {
private static final Log LOGGER = LogFactory.getLog(MainApp.class);
public static void main(String[] args) {
//获取 ApplicationContext 容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//获取名为 employee 的 Bean
Employee employee = context.getBean("employee", Employee.class);
//通过日志打印信息
LOGGER.info(employee.toString());
}
}
执行 MainApp 中的 main 方法,控制台输出如下:
4月 08, 2022 10:48:31 上午 section1.demo3.MainApp main
信息: Employee{empNo='9527', empName='传陆', dept=Department{deptNo='007', deptName='研发部'}}
c 命名空间注入
c 命名空间是构造函数注入的一种快捷实现方式。通过它,我们能够以 <bean>
属性的形式实现构造函数方式的属性注入,而不再使用嵌套的 <constructor-arg>
首先我们需要在配置文件的 <beans>
xmlns:c="http://www.springframework.org/schema/c"
在导入 XML 约束后,我们就能通过以下形式实现属性注入。
<bean id="Bean 唯一标志符" class="包名 + 类名" c:普通属性="普通属性值" c:对象属性-ref="对象的引用">
使用 c 命名空间注入依赖时,必须注意以下 2 点:
- Java 类中必须包含对应的带参构造器;
- 在使用 c 命名空间实现属性注入前,XML 配置的
<beans>
例 4
下面我们通过一个简单的实例,演示下如何通过 c 命名空间实现属性注入。
创建命名为 Department 的类,添加一个有参构造函数,代码如下:
package section1.demo4;
/**
* ClassName: Department
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class Department {
// 部门编号
private String deptNo;
// 部门名称
private String deptName;
//带参构造函数
public Department(String deptNo, String deptName) {
this.deptNo = deptNo;
this.deptName = deptName;
}
public String getDeptNo() {
return deptNo;
}
public void setDeptNo(String deptNo) {
this.deptNo = deptNo;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
@Override
public String toString() {
return "Dept{" +
"deptNo='" + deptNo + '\'' +
", deptName='" + deptName + '\'' +
'}';
}
}
创建命名为 Employee 的类,添加一个有参构造函数,代码如下:
package section1.demo4;
/**
* ClassName: Student
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class Employee {
// 员工编号
private String empNo;
// 员工姓名
private String empName;
// 员工所属部门
private Department dept;
// 带参构造函数
public Employee(String empNo, String empName, Department dept) {
this.empNo = empNo;
this.empName = empName;
this.dept = dept;
}
public String getEmpNo() {
return empNo;
}
public void setEmpNo(String empNo) {
this.empNo = empNo;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Employee{" +
"empNo='" + empNo + '\'' +
", empName='" + empName + '\'' +
", dept=" + dept +
'}';
}
}
修改 Spring 配置文件 spring-config.xml,使用 c 命名空间实现属性注入,代码如下:
<?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:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dept" class="section1.demo4.Department" c:deptNo="007" c:deptName="研发部"/>
<bean id="employee" class="section1.demo4.Employee" c:empName="传陆" c:dept-ref="dept" c:empNo="9527"/>
</beans>
创建命名为 MainApp 的类,代码如下:
package section1.demo4;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* ClassName: MainApp
* Description: TODO
*
* @author chuanlu
* @version 1.0.0
*/
public class MainApp {
private static final Log LOGGER = LogFactory.getLog(MainApp.class);
public static void main(String[] args) {
//获取 ApplicationContext 容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//获取名为 employee 的 Bean
Employee employee = context.getBean("employee", Employee.class);
//通过日志打印信息
LOGGER.info(employee.toString());
}
}
- 执行 MainApp 中的 main 方法,控制台输出如下:
4月 08, 2022 11:03:48 上午 section1.demo4.MainApp main
信息: Employee{empNo='9527', empName='传陆', dept=Dept{deptNo='007', deptName='研发部'}}