7.2 Spring Bean 注入内部 Bean

我们将定义在 <bean> 元素的 <property><constructor-arg> 元素内部的 Bean,称为 内部 Bean

1. 构造函数方式注入

我们可以通过构造方法注入内部 Bean。此时,我们只需要在 <bean> 标签下的 <constructor-arg> 元素中,再次使用 <bean>

格式如下:

<bean id="……" class="……">
    <constructor-arg name="……">
        <!--内部 Bean-->
        <bean class="……">
            <constructor-arg name="……" value="……"></constructor-arg>
            ……
        </bean>
    </constructor-arg>
</bean>
例 1

下面我们就通过一个实例,演示下如何在通过构造方法的方式注入内部 Bean。

创建命名为 Department 的类,代码如下:

package section2.demo1;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * ClassName: Department
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public class Department {
    private static final Log LOGGER = LogFactory.getLog(Department.class);
    // 部门编号
    private String deptNo;
    // 部门名称
    private String deptName;

    public Department(String deptNo, String deptName) {
        LOGGER.info("正在执行 Course 的有参构造方法,参数分别为:deptNo=" + deptNo + ",deptName=" + deptName);
        this.deptNo = deptNo;
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptNo='" + deptNo + '\'' +
                ", deptName='" + deptName + '\'' +
                '}';
    }
}

创建命名为 Employee 的类,代码如下:

package section2.demo1;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * ClassName: Employee
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public class Employee {
    private static final Log LOGGER = LogFactory.getLog(Employee.class);
    // 员工编号
    private String empNo;
    // 员工姓名
    private String empName;
    // 员工所属部门
    private Department dept;

    public Employee(String empNo, String empName, Department dept) {
        LOGGER.info("正在执行 Course 的有参构造方法,参数分别为:empNo=" + empNo + ",empName=" + empName + ",dept=" + dept);
        this.empNo = empNo;
        this.empName = empName;
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "empNo='" + empNo + '\'' +
                ", empName='" + empName + '\'' +
                ", dept=" + dept +
                '}';
    }
}

修改 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="employee" class="section2.demo1.Employee">
        <constructor-arg name="empNo" value="9527"/>
        <constructor-arg name="empName" value="传陆"/>
        <constructor-arg name="dept">
            <!--内部 Bean-->
            <bean class="section2.demo1.Department">
                <constructor-arg name="deptNo" value="007"/>
                <constructor-arg name="deptName" value="研发部"/>
            </bean>
        </constructor-arg>
    </bean>
</beans>

创建命名为 MainApp 的类,代码如下:

package section2.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");
        //获取名为 employee 的 Bean
        Employee employee = context.getBean("employee", Employee.class);
        //通过日志打印信息
        LOGGER.info(employee.toString());
    }
}

执行 MainApp 中的 main 方法,控制台输出如下:

4月 08, 2022 12:14:15 下午 section2.demo1.Department <init>
信息: 正在执行 Course 的有参构造方法,参数分别为:deptNo=007,deptName=研发部
4月 08, 2022 12:14:15 下午 section2.demo1.Employee <init>
信息: 正在执行 Course 的有参构造方法,参数分别为:empNo=9527,empName=传陆,dept=Dept{deptNo='007', deptName='研发部'}
4月 08, 2022 12:14:15 下午 section2.demo1.MainApp main
信息: Employee{empNo='9527', empName='传陆', dept=Dept{deptNo='007', deptName='研发部'}}

2. setter 方法注入

我们可以通过 setter 方法注入内部 Bean。此时,我们只需要在 <bean> 标签下的 <constructor-arg> 元素中,再次使用 <bean>

格式如下:

<bean id="outerBean" class="……">
    <property name="……" >
        <!-- 定义内部 Bean -->
        <bean class="……">
            <property name="……" value="……" ></property>
            ……
        </bean>
    </property>
</bean>

注意:内部 Bean 都是匿名的,不需要指定 id 和 name 的。即使制定了,IoC 容器也不会将它作为区分 Bean 的标识符,反而会无视 Bean 的 Scope 标签。因此内部 Bean 几乎总是匿名的,且总会随着外部的 Bean 创建。内部 Bean 是无法被注入到它所在的 Bean 以外的任何其他 Bean 的。

例 2

下面我们就通过一个实例,演示下如何使用 setter 方法注入内部 Bean。

创建命名为 Department 的类,代码如下:

package section2.demo2;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * ClassName: Department
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public class Department {
    private static final Log LOGGER = LogFactory.getLog(Department.class);
    // 部门编号
    private String deptNo;
    // 部门名称
    private String deptName;

    // 无参构造方法,在没有其他带参构造方法的情况下,可以省略
    public Department() {
    }

    public void setDeptNo(String deptNo) {
        LOGGER.info("正在执行 Department 类的 setDeptNo() 方法…… ");
        this.deptNo = deptNo;
    }

    public void setDeptName(String deptName) {
        LOGGER.info("正在执行 Department 类的 setDeptName() 方法…… ");
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptNo='" + deptNo + '\'' +
                ", deptName='" + deptName + '\'' +
                '}';
    }
}

创建命名为 Employee 的类,代码如下:

package section2.demo2;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * ClassName: Employee
 * Description: TODO
 *
 * @author chuanlu
 * @version 1.0.0
 */
public class Employee {
    private static final Log LOGGER = LogFactory.getLog(Employee.class);
    // 员工编号
    private String empNo;
    // 员工姓名
    private String empName;
    // 部门信息
    private Department dept;

    // 无参构造方法,在没有其他带参构造方法的情况下,可以省略
    public Employee() {
    }

    public void setEmpNo(String empNo) {
        LOGGER.info("正在执行 Employee 类的 setEmpNo() 方法…… ");
        this.empNo = empNo;
    }

    public void setEmpName(String empName) {
        LOGGER.info("正在执行 Employee 类的 setEmpName() 方法…… ");
        this.empName = empName;
    }

    public void setDept(Department dept) {
        LOGGER.info("正在执行 Employee 类的 setDept() 方法…… ");
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "empNo='" + empNo + '\'' +
                ", empName='" + empName + '\'' +
                ", dept=" + dept +
                '}';
    }
}

修改 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="employee" class="section2.demo2.Employee">
        <property name="empNo" value="9527"/>
        <property name="empName" value="小王"/>
        <property name="dept">
            <!--内部 Bean-->
            <bean class="section2.demo2.Department">
                <property name="deptNo" value="007"/>
                <property name="deptName" value="研发部"/>
            </bean>
        </property>
    </bean>

</beans>

创建命名为 MainApp 的类,代码如下:

package section2.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");
        //获取名为 employee 的 Bean
        Employee employee = context.getBean("employee", Employee.class);
        //通过日志打印信息
        LOGGER.info(employee.toString());
    }
}

执行 MainApp 中的 main 方法,控制台输出如下:

4月 08, 2022 12:20:00 下午 section2.demo2.Department setDeptNo
信息: 正在执行 Department 类的 setDeptNo() 方法…… 
4月 08, 2022 12:20:00 下午 section2.demo2.Department setDeptName
信息: 正在执行 Department 类的 setDeptName() 方法…… 
4月 08, 2022 12:20:00 下午 section2.demo2.Employee setEmpNo
信息: 正在执行 Employee 类的 setEmpNo() 方法…… 
4月 08, 2022 12:20:00 下午 section2.demo2.Employee setEmpName
信息: 正在执行 Employee 类的 setEmpName() 方法…… 
4月 08, 2022 12:20:00 下午 section2.demo2.Employee setDept
信息: 正在执行 Employee 类的 setDept() 方法…… 
4月 08, 2022 12:20:00 下午 section2.demo2.MainApp main
信息: Employee{empNo='9527', empName='小王', dept=Dept{deptNo='007', deptName='研发部'}}