JavaWeb开发(三)3.3——Spring Bean详解(基于XML方式)

一、Bean的概念

由 Spring IoC 容器负责创建、管理所有的Java对象,这些管理的对象称为 Bean,Bean 根据 Spring 配置文件中的信息创建。

二、基于XML方式管理bean对象

eg:

<?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="userEntity" class="com.qizekj.entity.UserEntity"></bean>

</beans>

XML 配置的 元素中可以包含多个属性或子元素,常用的属性或子元素如下表所示:

  • id:Bean 的唯一标识符,Spring IoC 容器对 Bean 的配置和管理都通过该属性完成。id 的值必须以字母开始,可以使用字母、数字、下划线等符号,不允许重复的。一般命名格式为类名称首字母小写。
  • name:该属性表示 Bean 的名称,我们可以通过 name 属性为同一个 Bean 同时指定多个名称,每个名称之间用逗号或分号隔开。Spring 容器可以通过 name 属性配置和管理容器中的 Bean。
  • class:该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,即类的全限定名。
  • scope:表示 Bean 的作用域,属性值可以为 singleton(单例)、prototype(原型)、request、session 和 global Session。默认值是 singleton。
  • constructor-arg: 元素的子元素,我们可以通过该元素,将构造参数传入,以实现 Bean 的实例化。该元素的 index 属性指定构造参数的序号(从 0 开始),type 属性指定构造参数的类型。
  • property:元素的子元素,用于调用 Bean 实例中的 setter 方法对属性进行赋值,从而完成属性的注入。该元素的 name 属性用于指定 Bean 实例中相应的属性名。
  • ref: 和 等元素的子元索,用于指定对某个 Bean 实例的引用,即 元素中的 id 或 name 属性。
  • value: 和 等元素的子元素,用于直接指定一个常量值。
  • list:用于封装 List 或数组类型的属性注入。
  • set:用于封装 Set 类型的属性注入。
  • map:用于封装 Map 类型的属性注入。
  • entry: 元素的子元素,用于设置一个键值对。其 key 属性指定字符串类型的键值,ref 或 value 子元素指定其值。
  • init-method:容器加载 Bean 时调用该方法,类似于 Servlet 中的 init() 方法。
  • destroy-method:容器删除 Bean 时调用该方法,类似于 Servlet 中的 destroy() 方法。该方法只在 scope=singleton 时有效。
  • lazy-init 懒加载,值为 true,容器在首次请求时才会创建 Bean 实例;值为 false,容器在启动时创建 Bean 实例。该方法只在 scope=singleton 时有效。

三、Bean属性注入

Bean 属性注入,就是将属性注入到 Bean 中的过程,而这属性既可以普通属性,也可以是一个对象(Bean)。
DI 依赖注入:对象的属性注入值;(spring实现)。

3.1、有参构造函数注入属性值

使用构造函数实现属性注入大致步骤如下:
(1)在 Bean 中添加一个有参构造函数,构造函数内的每一个参数代表一个需要注入的属性;
eg:

public class OrderEntity {
    private String orderId;
    private String orderName;

    public OrderEntity(String orderId, String orderName) {
        this.orderId = orderId;
        this.orderName = orderName;
    }

    @Override
    public String toString() {
        return "OrderEntity{" +
                "orderId='" + orderId + '\'' +
                ", orderName='" + orderName + '\'' +
                '}';
    }
}

(2)在 Spring 的 XML 配置文件中,通过 及其子元素 对 Bean 进行定义;
(3)在 元素内使用 元素,对构造函数内的属性进行赋值,Bean 的构造函数内有多少参数,就需要使用多少个 元素。
eg:

<!-- 第一种方式:使用name,指定参数列表名称: -->
<bean id="orderEntity" class="com.qizekJ.entity.OrderEntity">
	<constructor-arg name="orderId" value="1"></constructor-arg>
    <constructor-arg name="orderName" value="订单1"></constructor-arg>
</bean>
<!-- 第二种方式:使用index,指定参数列表索引: -->
<bean id="orderEntity" class="com.qizekJ.entity.OrderEntity">
    <constructor-arg index="0" value="2"></constructor-arg>
    <constructor-arg index="1" value="订单2"></constructor-arg>
</bean>

3.2、setter 注入属性值

在 Spring 实例化 Bean 的过程中,IoC 容器首先会调用默认的构造方法(无参构造方法)实例化 Bean(Java 对象),然后通过 Java 的反射机制调用这个 Bean 的 setXxx() 方法,将属性值注入到 Bean 中。

使用 setter 注入的方式进行属性注入,大致步骤如下:
(1)在 Bean 中提供一个默认的无参构造函数(在没有其他带参构造函数的情况下,可省略),并为所有需要注入的属性提供一个 setXxx() 方法;
eg:

public class OrderEntity {
    private String orderId;
    private String orderName;

    public OrderEntity() {
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
    }

    @Override
    public String toString() {
        return "OrderEntity{" +
                "orderId='" + orderId + '\'' +
                ", orderName='" + orderName + '\'' +
                '}';
    }
}

(2)在 Spring 的 XML 配置文件中,使用 及其子元素 对 Bean 进行定义;
(3)在 元素内使用 元素对各个属性进行赋值。
eg:

<bean id="bookEntity" class="com.qizekj.entity.OrderEntity">
    <property name="orderId" value="3"></property>
    <property name="orderName" value="订单3"></property>
</bean>

3.3、p 命名空间注入属性值

p 命名空间是 setter 方式属性注入的一种快捷实现方式。通过它,我们能够以 bean 属性的形式实现 setter 方式的属性注入,而不再使用嵌套的 元素,以实现简化 Spring 的 XML 配置的目的。

使用p 命名空间实现属性注入大致步骤如下:
(1)在配置文件的 元素中导入以下 XML 约束:

xmlns:p="http://www.springframework.org/schema/p"

(2)在导入 XML 约束后,我们就能通过以下形式实现属性注入:

 <bean id="Bean 唯一标志符" class="包名+类名" p:普通属性="普通属性值" p:对象属性-ref="对象的引用">

eg:

<bean id="orderEntity" class="com.qizekj.entity.OrderEntity" p:orderId="4" p:orderName="订单4">
</bean>

使用 p 命名空间注入依赖时,须注意以下 3 点:

  • Java 类中必须有 setter 方法;
  • Java 类中必须有无参构造器(类中不包含任何带参构造函数的情况,无参构造函数默认存在);
  • 在使用 p 命名空间实现属性注入前,XML 配置的 元素内必须先导入 p 命名空间的 XML 约束。

注入空值属性:

<bean id="OrderEntity" class="com.qizekj.entity.OrderEntity">
    <property name="orderId" value="5">
    </property>
    <property name="orderName" >
        <null></null>
    </property>
</bean>

注入特殊符号:
eg:
(1)转移注入方式:

<!--  转移注入方式 -->
<bean id="OrderEntity" class="com.qizekj.entity.OrderEntity">
    <property name="orderId" value="&lt;&lt;6&gt;&gt;"></property>
    <property name="orderName">
        <null></null>
    </property>
</bean>

(2)Cdata注入方式:

<!--  Cdata注入方式 -->
<bean id="OrderEntity" class="com.qizekj.entity.OrderEntity">
    <property name="orderId">
        <value><![CDATA[<<7>>]]></value>
    </property>
    <property name="orderName">
        <null></null>
    </property>
</bean>

3.4、c 命名空间注入

c 命名空间是构造函数注入的一种快捷实现方式。通过它,我们能够以 属性的形式实现构造函数方式的属性注入,而不再使用嵌套的 元素,以实现简化 Spring 的 XML 配置的目的。

使用c 命名空间实现属性注入大致步骤如下:
(1)在配置文件的 元素中导入以下 XML 约束:

xmlns:c="http://www.springframework.org/schema/c"

(2)在导入 XML 约束后,我们就能通过以下形式实现属性注入:

<bean id="Bean 唯一标志符" class="包名+类名" c:普通属性="普通属性值" c:对象属性-ref="对象的引用">

使用 c 命名空间注入依赖时,必须注意以下 2 点:

  • Java 类中必须包含对应的带参构造器;
  • 在使用 c 命名空间实现属性注入前,XML 配置的 元素内必须先导入 c 命名空间的 XML 约束。

3.5、注入外部bean

接口UserDao:

public interface UserDao {
    void addUser();
}

实现UserDao接口:

public class UserDaoImpl implements  UserDao {
   public void addUser() {
       System.out.println("执行addUser");
    }
}

UserService业务类:

import com.qizekj.dao.UserDao;
import com.qizekj.dao.UserDaoImpl;

public class UserService {
    private UserDao userDao;

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser() {
        userDao.addUser();
    }
}

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">
    
    <!-- 注入useService  -->
    <bean id="userService" class="com.qizekj.service.UserService">
        <!--  注入userDao,name 属性值: 类中属性的名称;ref:创建UserDaoImpl类的 bean的id -->
        <property name="userDao" ref="userDao"></property>
    </bean>
    <bean id="userDao" class="com.qizekj.dao.UserDaoImpl"></bean>
</beans>

3.5、注入内部bean

员工对象:

public class EmpEntity {

    private String name;
    private Integer age;
    /**
     * 员工属于那个部门
     */
    private  DeptEntity deptEntity;
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public void setDeptEntity(DeptEntity deptEntity) {
        this.deptEntity = deptEntity;
    }

    @Override
    public String toString() {
        return "EmpEntity{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", deptEntity=" + deptEntity +
                '}';
    }
}

部门对象:

public class DeptEntity {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "DeptEntity{" +
                "name='" + name + '\'' +
                '}';
    }
}

Xml相关配置:

<!--内部bean -->
<bean id="empEntity" class="com.qizekj.entity.EmpEntity">
    <!--设置属性name/age -->
    <property name="name" value="qizekj"></property>
    <property name="age" value="32"></property>
    <!-- 嵌入部门bean-->
    <property name="deptEntity">
        <bean id="deptEntity" class="com.qizekj.entity.DeptEntity">
            <property name="name" value="技术部"></property>
        </bean>
    </property>
</bean>

3.6、注入级联赋值

方式一:

<bean id="empEntity" class="com.qizekj.entity.EmpEntity">
    <!--两个属性-->
    <property name="name" value="qizekj"></property>
    <property name="addres" value="安徽省合肥市"></property>
    <!--级联赋值-->
    <property name="deptEntity" ref="deptEntity"></property>
</bean>
<bean id="deptEntity" class="com.qizekj.entity.DeptEntity">
    <property name="name" value="技术部"></property>
</bean>

方式二:
方式二注意:需要在员工类增加deptEntity的get方法。

<bean id="empEntity" class="com.qizekj.entity.EmpEntity">
    <!--两个属性-->
    <property name="name" value="qizekj"></property>
    <property name="addres" value="安徽省合肥市"></property>
    <!--级联赋值-->
    <property name="deptEntity" ref="deptEntity"></property>
    <property name="deptEntity.name" value="技术部"></property>
</bean>
<bean id="deptEntity" class="com.qizekj.entity.DeptEntity">
</bean>

3.7、注入数组、集合类型属性

学生类:

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class StuEntity {
    //1.数组属性
    private String[] arrays;
    //2.list集合属性
   private List<String> list;
    //3.Map
    private Map<String,String> map;
    //4.Set
    private Set<String> set;

    public void setArrays(String[] arrays) {
        this.arrays = arrays;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    @Override
    public String toString() {
        return "StuEntity{" +
                "arrays=" + Arrays.toString(arrays) +
                ", list=" + list +
                ", map=" + map +
                ", set=" + set +
                '}';
    }
}

xml配置文件:

<bean id="stuEntity" class="com.mayikt.entity.StuEntity">
    <!--数组类型注入-->
    <property name="arrays">
        <array>
            <value>mayikt01</value>
            <value>mayikt02</value>
        </array>
    </property>
    <!--list-->
    <property name="list">
        <list>
            <value>语文</value>
            <value>数学</value>
        </list>
    </property>
    <!--Map-->
    <property name="map">
       <map>
           <entry key="余胜军" value="23"></entry>
           <entry key="小薇" value="25"></entry>
       </map>
    </property>
    <!--Set-->
    <property name="Set">
       <set>
           <value>01</value>
           <value>02</value>
       </set>
    </property>
</bean>

四、IOC操作Bean的管理

Spring中两种类型bean,一种是为普通bean,另外一种是工厂bean:
普通Bean:在配置文件中定义什么类型与返回的类型需一致;
工厂Bean:在配置文件中定义Bean类型与返回类型可以不一致。

eg:
创建一个工厂Bean类,实现FactoryBean接口

public class QizekjBean implements FactoryBean {

    public UserEntity getObject() throws Exception {
        return new UserEntity();
    }

    public Class<?> getObjectType() {
        return null;
    }
}

配置文件spring_factory.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">

    <!--spring ioc管理-->
    <bean id="qizekjBean" class="com.qizekj.factorybean.QizekjBean"> </bean>
</beans>

测试类:

public class factoryTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("spring_factory.xml");
        //QizekjBean qizekjBean = (QizekjBean) classPathXmlApplicationContext.getBean("qizekjBean");
        UserEntity qizekjBean = (UserEntity) classPathXmlApplicationContext.getBean("qizekjBean");
        System.out.println(qizekjBean);
    }
}

当工厂Bean类实现FactoryBean接口,重写了getObject()方法,返回类型为UserEntity时,测试类中再用xml配置的QizekjBean就会报错了。

五、Bean作用域

单例的作用域:每次在调用getbean方法获取对象都是同一个对象;
多例的作用域:每次在调用getbean方法获取对象都是一个新的对象。

默认情况下,所有的 Spring Bean 都是单例的,也就是说在整个 Spring 应用中, Bean 的实例只有一个。
在 元素中添加 scope 属性来配置 Spring Bean 的作用范围。

单例:在同一个jvm中,该bean对象只会创建一次;
多例:在同一个jvm中,该bean对象可以被创建多次。

Spring 5 共提供了 6 种 scope 作用域:

  • singleton:默认值,单例模式,表示在 Spring 容器中只有一个 Bean 实例。
  • prototype:原型模式,表示每次通过 Spring 容器获取 Bean 时,容器都会创建一个新的 Bean 实例。
  • request:每次 HTTP 请求,容器都会创建一个 Bean 实例。该作用域只在当前 HTTP Request 内有效。
  • session:同一个 HTTP Session 共享一个 Bean 实例,不同的 Session 使用不同的 Bean 实例。该作用域仅在当前 HTTP Session 内有效。
  • application:同一个 Web 应用共享一个 Bean 实例,该作用域在当前 ServletContext 内有效。 与 singleton 类似,但 singleton 表示每个 IoC 容器中仅有一个 Bean 实例,而一个 Web 应用中可能会存在多个 IoC 容器,但一个 Web 应用只会有一个 ServletContext,也可以说 application 才是 Web 应用中货真价实的单例模式。
  • websocket:websocket 的作用域是 WebSocket ,即在整个 WebSocket 中有效。

注意:在以上 6 种 Bean 作用域中,除了 singleton 和 prototype 可以直接在常规的 Spring IoC 容器(例如 ClassPathXmlApplicationContext)中使用外,剩下的都只能在基于 Web 的 ApplicationContext 实现(例如 XmlWebApplicationContext)中才能使用,否则就会抛出一个 IllegalStateException 的异常。

(1)singleton

singleton 是 Spring 容器默认的作用域。当 Bean 的作用域为 singleton 时,Spring IoC 容器中只会存在一个共享的 Bean 实例。这个 Bean 实例将存储在高速缓存中,所有对于这个 Bean 的请求和引用,只要 id 与这个 Bean 定义相匹配,都会返回这个缓存中的对象实例。
如果一个 Bean 定义的作用域为 singleton ,那么这个 Bean 就被称为 singleton bean。在 Spring IoC 容器中,singleton bean 是 Bean 的默认创建方式,可以更好地重用对象,节省重复创建对象的开销。

在 Spring 配置文件中,可以使用 元素的 scope 属性,将 Bean 的作用域定义成 singleton,其配置方式如下所示:

<bean id="..." class="..." scope="singleton"/>

(2)prototype

如果一个 Bean 定义的作用域为 prototype,那么这个 Bean 就被称为 prototype bean。对于 prototype bean 来说,Spring 容器会在每次请求该 Bean 时,都创建一个新的 Bean 实例。

在 Spring 配置文件中,可以使用 元素的 scope 属性将 Bean 的作用域定义成 prototype,其配置方式如下所示:

<bean id="..." class="..." scope="prototype"/>

eg:
配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<?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">

    <!--spring ioc管理-->
    <bean id="qizekjBean" class="com.qizekj.factorybean.QizekjBean"> </bean>
</beans>

测试类:

public class factoryTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("spring_factory.xml");
        QizekjBean qizekjBean1 = (QizekjBean) classPathXmlApplicationContext.getBean("qizekjBean");
        QizekjBean qizekjBean2 = (QizekjBean) classPathXmlApplicationContext.getBean("qizekjBean");
        System.out.println(qizekjBean1);
        System.out.println(qizekjBean2);
        System.out.println(qizekjBean1 == qizekjBean2);
    }
}

结果:
userEntity1与userEntity2完全相等
在这里插入图片描述

将配置文件中bean改为多例模式:

<?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">

    <!--spring ioc管理-->
    <bean id="qizekjBean" class="com.qizekj.factorybean.QizekjBean" scope="prototype"> </bean>
</beans>

运行测试类,得到结果为:
userEntity1与userEntity2不相等,不是同一个bean
在这里插入图片描述

六、Bean继承

在 Spring 中,Bean 和 Bean 之间也存在继承关系。我们将被继承的 Bean 称为父 Bean,将继承父 Bean 配置信息的 Bean 称为子 Bean。
Spring Bean 的定义中可以包含很多配置信息,例如构造方法参数、属性值。子 Bean 既可以继承父 Bean 的配置数据,也可以根据需要重写或添加属于自己的配置信息。

在 Spring XML 配置中,我们通过子 Bean 的 parent 属性来指定需要继承的父 Bean,配置格式如下:

<!--父Bean-->
<bean id="parentBean" class="xxx.xxxx.xxx.ParentBean" >
    <property name="xxx" value="xxx"></property>
    <property name="xxx" value="xxx"></property>
</bean> 
<!--子Bean--> 
<bean id="childBean" class="xxx.xxx.xxx.ChildBean" parent="parentBean"></bean>

在父 Bean 的定义中,有一个十分重要的属性,那就是 abstract 属性。如果一个父 Bean 的 abstract 属性值为 true,则表明这个 Bean 是抽象的。
抽象的父 Bean 只能作为模板被子 Bean 继承,它不能实例化,也不能被其他 Bean 引用,更不能在代码中根据 id 调用 getBean() 方法获取,否则就会返回错误。
在父 Bean 的定义中,既可以指定 class 属性,也可以不指定 class 属性。如果父 Bean 定义没有明确地指定 class 属性,那么这个父 Bean 的 abstract 属性就必须为 true。

七、Spring Bean的生命周期

7.1 Bean的生命周期

周期简单分为:实例化→属性赋值→初始化→销毁
生命周期的原理:
(1)通过构造函数创建bean对象(默认执行无参构造函数,底层基于反射实现)
(2)为bean的属性赋值 (使用反射调用set方法)
(3)调用bean的初始化的方法(需要单独在类中配置初始化的方法)
(4)正常使用bean对象
(5)Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)

eg:

public class MemberEntity {
    private String name;
    public MemberEntity(){
        System.out.println("第一步:————通过构造函数创建bean对象,无参构造函数被执行");
    }

    public void setName(String name) {
        System.out.println("第二步:为bean的属性赋值,set方法初始化属性");
        this.name = name;
    }

    public void initMethod(){
        System.out.println("第三步:调用bean的初始化的方法,回调调用init初始化方法");
    }

    public void destroyMethod(){
        System.out.println("第五步:Spring容器关闭,调用该类的销毁回调的方法,回调调用destroyMethod方法");
    }
}

配置文件,配置bean并赋值,设置initMethod、destroyMethod方法:

<?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="memberEntity" class="com.qizekj.entity.MemberEntity" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="qizekj"></property>
    </bean>
</beans>

测试类:

public static void main(String[] args) {
    ClassPathXmlApplicationContext classPathXmlApplicationContext =
            new ClassPathXmlApplicationContext("spring_02.xml");
    MemberEntity memberEntity = (MemberEntity) classPathXmlApplicationContext.getBean("memberEntity");
    System.out.println("第四步:获取使用到的memberEntity,正常使用bean对象");
    System.out.println(memberEntity);
    // 手动让bean容器销毁
    classPathXmlApplicationContext.close();
}

运行结果:
在这里插入图片描述

7.2 Bean的后置处理器

Bean的后置处理器: 作用提供更多的扩展功能。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @author chenqun
 * @date 2023/1/14 17:11
 */
public class QizekjBeanPost implements BeanPostProcessor {
    /**
     * 调用初始化方法之前执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在bean 初始化方法之前执行");
        return bean;
    }

    /**
     * 调用初始化方法之后执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在bean 初始化方法之后执行");
        return bean;
    }
}

配置文件:

<?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="memberEntity" class="com.qizekj.entity.MemberEntity" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="qizekj"></property>
    </bean>

    <bean id="mayiktBeanPost" class="com.qizekj.entity.QizekjBeanPost"></bean>
</beans>

测试方法:

public class Test04 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("spring_02.xml");
        MemberEntity memberEntity = (MemberEntity) classPathXmlApplicationContext.getBean("memberEntity");
        System.out.println("第四步:获取使用到的memberEntity,正常使用bean对象");
        System.out.println(memberEntity);
        // 手动让bean容器销毁
        classPathXmlApplicationContext.close();
    }
}

运行结果:
在这里插入图片描述

7.3 Bean的后置处理器底层原理

配置多个BeanPostProcessor:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;

/**
 * @author chenqun
 * @date 2023/1/14 17:11
 */
public class QizekjBeanPost implements BeanPostProcessor, Ordered {
    /**
     * 调用初始化方法之前执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在bean 初始化方法之前执行");
        return bean;
    }

    /**
     * 调用初始化方法之后执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在bean 初始化方法之后执行");
        return bean;
    }

    public int getOrder() {
        return 1;
    }
}
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;

/**
 * @author chenqun
 * @date 2023/1/14 17:18
 */
public class QizekjBeanPost2 implements BeanPostProcessor, Ordered {
    /**
     * 调用初始化方法之前执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("QizekjBeanPost2:在bean 初始化方法之前执行");
        return bean;
    }

    /**
     * 调用初始化方法之后执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("QizekjBeanPost2:在bean 初始化方法之后执行");
        return bean;
    }

    public int getOrder() {
        return 0;
    }
}

配置文件:

<?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="memberEntity" class="com.qizekj.entity.MemberEntity" init-method="initMethod" destroy-method="destroyMethod">
        <property name="name" value="qizekj"></property>
    </bean>

    <!-- 后置处理器-->
    <bean id="mayiktBeanPost" class="com.qizekj.entity.QizekjBeanPost"></bean>
    <bean id="mayiktBeanPost2" class="com.qizekj.entity.QizekjBeanPost2"></bean>
</beans>

测试类:

public class Test04 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("spring_02.xml");
        MemberEntity memberEntity = (MemberEntity) classPathXmlApplicationContext.getBean("memberEntity");
        System.out.println("第四步:获取使用到的memberEntity,正常使用bean对象");
        System.out.println(memberEntity);
        // 手动让bean容器销毁
        classPathXmlApplicationContext.close();
    }
}

运行结果:
实现Ordered接口的getOrder方法,值越小越优先加载。
在这里插入图片描述

八、SpringBean的自动装配

把 Spring 在 Bean 与 Bean 之间建立依赖关系的行为称为“装配”。
Spring 的 IOC 容器虽然功能强大,但它本身不过只是一个空壳而已,它自己并不能独自完成装配工作。需要我们主动将 Bean 放进去,并告诉它 Bean 和 Bean 之间的依赖关系,它才能按照我们的要求完成装配工作。
前面描述的都是在 XML 配置中通过 和 中的 ref 属性,手动维护 Bean 与 Bean 之间的依赖关系的。对于只包含少量 Bean 的应用来说,这种方式已经足够满足我们的需求了。但随着应用的不断发展,容器中包含的 Bean 会越来越多,Bean 和 Bean 之间的依赖关系也越来越复杂,这就使得我们所编写的 XML 配置也越来越复杂,越来越繁琐。过于复杂的 XML 配置不但可读性差,而且编写起来极易出错,严重的降低了开发人员的开发效率。为了解决这一问题,Spring 框架还提供了“自动装配”功能。

Spring 的自动装配功能可以让 Spring 容器依据某种规则(自动装配的规则,有五种),为指定的 Bean 从应用的上下文(AppplicationContext 容器)中查找它所依赖的 Bean,并自动建立 Bean 之间的依赖关系。而这一过程是在完全不使用任何 和 元素 ref 属性的情况下进行的。
Spring 的自动装配功能能够有效地简化 Spring 应用的 XML 配置,因此在配置数量相当多时采用自动装配降低工作量。
Spring 框架式默认不支持自动装配的,要想使用自动装配,则需要对 Spring XML 配置文件中 元素的 autowire 属性进行设置。

<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">
    <!--部门 Dept 的 Bean 定义-->
    <bean id="dept" class="com.qize.Dept"></bean>
   
    <!--员工 Employee 的 Bean 定义,通过 autowire 属性设置自动装配的规则-->
    <bean id="employee" class="com.qize.Employee" autowire="byName">
    </bean>
</beans>

Spring 共提供了 5 中自动装配规则,它们分别与 autowire 属性的 5 个取值对应,具体如下:

  • byName:按名称自动装配。Spring 会根据的 Java 类中对象属性的名称,在整个应用的上下文 ApplicationContext(IoC 容器)中查找。若某个 Bean 的 id 或 name 属性值与这个对象属性的名称相同,则获取这个 Bean,并与当前的 Java 类 Bean 建立关联关系。
  • byType:按类型自动装配。Spring 会根据 Java 类中的对象属性的类型,在整个应用的上下文 ApplicationContext(IoC 容器)中查找。若某个 Bean 的 class 属性值与这个对象属性的类型相匹配,则获取这个 Bean,并与当前的 Java 类的 Bean 建立关联关系。
  • constructor:与 byType 模式相似,不同之处在与它应用于构造器参数(依赖项),如果在容器中没有找到与构造器参数类型一致的 Bean,那么将抛出异常。其实就是根据构造器参数的数据类型,进行 byType 模式的自动装配。
  • default:表示默认采用上一级元素 设置的自动装配规则(default-autowire)进行装配。
  • no:默认值,表示不使用自动装配,Bean 的依赖关系必须通过 和 元素的 ref 属性来定义。

根据指定装配规则(属性名称或者属性的类型),spring自动将匹配属性的值注入。

(1)根据属性的名称注入(autowire=“byName”),bean的id名称与属性的名称一致:
eg:

<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 根据属性的名称注入,bean的id名称与属性的名称一致 -->
    <bean id="empEntity" class="com.qizekj.entity.EmpEntity" autowire="byName">
    </bean>
    
    <bean id="deptEntity" class="com.qizekj.entity.DeptEntity">
        <property name="name" value="技术部"></property>
    </bean>
</beans>

根据属性的类型注入(autowire=“byType”),bean的类型与属性类型一致:
eg:

<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 根据属性的类型注入,bean的类型与属性类型一致 -->
    <bean id="empEntity" class="com.mayikt.entity.EmpEntity" autowire="byType">
    </bean>
    
    <bean id="deptEntity" class="com.mayikt.entity.DeptEntity">
        <property name="name" value="教育部门"></property>
    </bean>
</beans>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吟诗作对歌一曲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值