Spring 6(一)

Spring 是什么

Spring 框架是一个功能强大的开源框架,主要用于简化企业级 Java 应用程序的开发。它提供了一套全面的基础架构,帮助开发者解决常见的编程问题,如依赖注入、面向方面编程(AOP)、事务管理和数据访问等。Spring 的核心是其灵活的依赖注入机制,这使得应用程序的各个组件可以更加松散地耦合,从而提高代码的可维护性和可测试性。

Spring 6 是 Spring 框架的最新版本,进一步优化了企业级 Java 应用的开发。它集成了最新的 Java 和 Jakarta EE 规范,支持 Java 17 及以上版本,提升了性能和安全性。Spring 6 还增强了对模块化架构的支持,通过更加简洁和现代化的编程模型,简化了开发流程。同时,它引入了对 GraalVM 和原生镜像的支持,使得应用启动更快、占用资源更少,为构建云原生应用提供了强有力的支持。

Spring 框架官网

要使用最基础的 Spring 的功能,需要引入以下依赖,由于我的项目中引入了 SpringBoot 做自动版本仲裁,所以这里我不用添加版本,如果需要指定版本,可以去中央仓库查询或查看官网:

<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
</dependency>

IoC 容器

概述

IoC(控制反转,Inversion of Control)是一种设计原则,用于减少代码之间的耦合性。它指的是将对象的创建和依赖的管理交由外部容器负责,而不是在类内部直接实例化对象或管理其依赖。IoC 容器就是 IoC 思想的一种落地实现。在 Spring 框架中,IoC 主要通过 依赖注入 (DI,Dependency Injection) 实现。

创建对象的过程

IoC 容器创建对象的过程简单来说,就是通过各种方式(例如注解、配置文件)告诉 Spring 容器如何创建和管理一个对象,然后 Spring 容器帮你把这个对象创建并保存起来。当你需要使用这个对象时,再从 Spring 容器中把对象取出来,这个过程就是 DI。

首先,如果想让 Spring 容器帮你创建一个对象,Spring 容器需要知道关于这个对象的信息,例如最基本的,需要知道这个对象属于哪个类。对象的信息,抽象成 Java 中的接口,就是:

org.springframework.beans.factory.config.BeanDefinition

有了对象信息,还不够,Spring 容器还要知道怎么得到这些类的信息(如 XML、注解、Java 配置),这些信息我们称为元信息。元信息最终会将其转换为 BeanDefinition 对象,供 Spring 容器使用,整个读取和加载的过程抽象成 Java 中的接口,就是:

org.springframework.beans.factory.support.BeanDefinitionReader

BeanDefinitionReader 有多个实现类,这些实现类定义了不同的读取元信息并加载 BeanDefinition 的方式:
在这里插入图片描述
Spring IoC 容器创建对象的过程中有很多细节,简单来说,容器首先会通过 BeanDefinitionReader 将不同来源的元信息转换成 BeanDefinition 对象(BeanDefinition 是可以有多个的,取决于你元信息的配置),然后将这些 BeanDefinition 对象注册到一个 BeanDefinitionRegistry 对象中,你可以认为 BeanDefinitionRegistry 对象就是一个保存 BeanDefinition 的容器。

org.springframework.beans.factory.support.BeanDefinitionRegistry

将 BeanDefinition 保存到 BeanDefinitionRegistry 后,容器会根据 BeanDefinitionRegistry 中保存的 BeanDefinition 信息,利用反射机制获取 Bean 的 Class 对象,然后创建对象,该对象最终会保存到一个 Map 中,变成一个 Spring Bean。

容器接口

所谓的 Spring 容器,最根本的接口其实是 BeanFactory,既然是一个容器,那么肯定需要从容器中获取一些东西,BeanFactory 就定义了一系列从容器中获取对象的方法。

org.springframework.beans.factory.BeanFactory

由于 BeanFactory 是最底层的接口,因此开发者一般不会直接使用,而是使用继承了 BeanFactory 的一些类或接口,例如 ApplicationContext,ApplicationContext 主要有以下的一些实现类:

类型名简介
ClassPathXmlApplicationContext通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
ConfigurableApplicationContextApplicationContext 的子接口,包含一些扩展方法 refresh() 和 close() ,让 ApplicationContext 具有启动、关闭和刷新上下文的能力。
WebApplicationContext专门为 Web 应用准备,基于 Web 环境创建 IOC 容器对象,并将对象引入存入 ServletContext 域中。

而其中最常用的就是 ClassPathXmlApplicationContext。

org.springframework.context.support.ClassPathXmlApplicationContext

以下是 ApplicationContext 的继承关系,由于层级过多,只展示了一部分:
在这里插入图片描述

基于 XML 管理 bean

基于 XML 管理 Bean,就是通过一个 XML 的配置文件告诉 Spring IoC 容器如何管理 Bean,可以使用 bean 标签定义一个bean,一个最基本的 Spring 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="helloWorld" class="com.zhumingjian.spring.entity.HelloWorld">
        <property name="message" value="Hello, Spring!"/>
    </bean>
</beans>

上面这个 XML 配置文件,告诉容器创建一个 HelloWorld 类的对象,并把这个对象的 message 实例变量赋值为 ”Hello Spring“,HelloWorld 类的代码如下:

public class HelloWorld {
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    public void getMessage() {
        System.out.println("Your Message: " + message);
    }
}

接下来需要创建 Spring 容器并告诉容器 XML 文件在哪:

public class Application {
    public static void main(String[] args) {
        //创建一个 Spring IoC 容器,告诉容器 XML 使用类路径下的 application.xml 文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
    }
}

获取 bean

根据 id 获取

由于在配置文件中的 bean 标签指定了 id 属性,所以可以根据 id 属性可以精确获取到一个 bean 对象

HelloWorld bean = (HelloWorld) context.getBean("helloWorld");

根据类型获取

当根据类型获取 bean 时,要求 IOC 容器中指定类型的 bean 有且只能有一个,否则会抛出 NoUniqueBeanDefinitionException 异常

HelloWorld bean = context.getBean(HelloWorld.class);

根据类型和 id 获取

当根据类型和 id 获取时,只要保证容器中指定 id 的对象是指定类型的就可以了

HelloWorld bean = context.getBean("helloWorld", HelloWorld.class);

结论

根据类型来获取 bean 时,在满足 bean 唯一性的前提下,其实只是看:『对象 instanceof 指定的类型』的返回结果,只要返回的是true 就可以认定为和类型匹配,能够获取到。

java 中,instanceof 运算符用于判断前面的对象是否是后面的类,或其子类、实现类的实例。如果是返回 true,否则返回 false。也就是说:用 instanceof 关键字做判断时, instanceof 操作符的左右操作必须有继承或实现关系。

依赖注入

所谓的依赖,可以简单理解为,一个类 A,需要用到另一个类 B 的实例,这时候 A 就对 B 产生了依赖。依赖注入就是容器在创建 A 对象的时候,将 B 对象的实例设置给 A,这样 A 就能用到 B 的东西了。

要实现这个需求,有两种方式,分别是通过 setter 方法和 构造器的方式注入,下面通过一个案例分别演示这两种注入方式,首先我们需要准备以下的类:

package com.zhumingjian.spring.entity;

public class Student {

    private Integer id;

    private String name;

    private Integer age;

    private String sex;

    public Student() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Student(Integer id, String name, Integer age, String sex) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

setter 注入

要通过 setter 方法注入依赖,首先保证注入的属性有 setter 方法,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="student" class="com.zhumingjian.spring.entity.Student">
        <!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
        <!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关) -->
        <!-- value属性:指定属性值 -->
        <property name="id" value="1001"/>
        <property name="name" value="张三"/>
        <property name="age" value="23"/>
        <property name="sex" value=""/>
    </bean>
</beans>

测试:

public class Application {
    public static void main(String[] args) {
        //创建一个 Spring IoC 容器,告诉容器 XML 使用类路径下的 application.xml 文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        Student bean = context.getBean(Student.class);
        //输出:Student{id=1001, name='张三', age=23, sex='男'}
        System.out.println(bean);
    }
}

构造器注入

要使用构造器注入,首先要保证有正确的构造器,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">
    <!--  constructor-arg 标签表示构造器的参数,其中 value 表示值,index 表示参数所在构造器的索引  -->
    <bean id="student" class="com.zhumingjian.spring.entity.Student">
        <constructor-arg value="1002" index="0"/>
        <constructor-arg value="李四" index="1"/>
        <constructor-arg value="33" index="2"/>
        <constructor-arg value="" index="3"/>
    </bean>
</beans>

测试:

public class Application {
    public static void main(String[] args) {
        //创建一个 Spring IoC 容器,告诉容器 XML 使用类路径下的 application.xml 文件
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        Student bean = context.getBean(Student.class);
        //输出:Student{id=1002, name='李四', age=33, sex='女'}
        System.out.println(bean);
    }
}

属性注入

如果一个类,没有 setter 方法,也没有对应参数的构造器,那么 Spring 就会尝试用反射机制获取字段,并赋值。

特殊值处理

null 值

如果要声明一个变量为 null,不能直接在 value 那写null,否则 Spring 会把它解析为字面量的null,应该使用以下方式:`

<property name="name">
    <null />
</property>
xml 实体

如果遇到一些特殊的符号,例如 < 和 > ,这些符号和 XML 语法冲突了,这时候要使用替代的符号,例如:

<!-- 小于号在XML文档中用来定义标签的开始,不能随便使用 -->
<!-- 解决方案一:使用XML实体来代替 -->
<property name="expression" value="a &lt; b"/>
CDATA 节

CDATA 也是解决 XML 语法冲突的方法之一,CDATA节中写什么符号都随意:

<property name="expression">
    <!-- 解决方案二:使用CDATA节 -->
    <!-- CDATA中的C代表Character,是文本、字符的含义,CDATA就表示纯文本数据 -->
    <!-- XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签或属性来解析 -->
    <!-- 所以CDATA节中写什么符号都随意 -->
    <value><![CDATA[a < b]]></value>
</property>

注入自定义对象

首先需要准备以下几个对象,其中 UserInfoController 依赖 UserService:

// 用户接口
public interface UserService {
    String getUserInfo();
}
// 用户接口实现
public class UserServiceImpl implements UserService {
    @Override
    public String getUserInfo() {
        return "用户信息";
    }
}
// 用户 Controller,在这个类中会使用 UserService,形成依赖
public class UserInfoController {
    private UserService userService;

    // setter方法注入方式
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public UserInfoController(UserService userService) {
        this.userService = userService;
    }

    public UserInfoController() {
        
    }

    public void test() {
        System.out.println(userService.getUserInfo());
    }
}
引用外部 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">
    <!--由于 UserInfoController 会用到 UserService,所以需要先创建 UserService 对象-->
    <bean id="userService" class="com.zhumingjian.spring.service.impl.UserServiceImpl"/>
    <!--创建 UserInfoController 对象-->
    <bean id="userInfoController" class="com.zhumingjian.spring.controller.UserInfoController">
        <!--将 UserService 引用给 UserInfoController-->
        <property name="userService" ref="userService"/>
        <!--或使用构造器注入-->
        <!--<constructor-arg name="userService" ref="userService"></constructor-arg>-->
    </bean>
</beans>
内部 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">
    <!--创建 UserInfoController 对象-->
    <bean id="userInfoController" class="com.zhumingjian.spring.controller.UserInfoController">
        <property name="userService">
            <!-- 在一个 bean 中再声明一个 bean 就是内部 bean -->
            <!-- 内部 bean 只能用于给属性赋值,不能在外部通过 IOC 容器获取,因此可以省略id属性 -->
            <bean class="com.zhumingjian.spring.service.impl.UserServiceImpl"/>
        </property>
    </bean>
</beans>

注入集合类型

准备一个测试类,只有简单的几个集合变量和对应的 setter 方法:

import java.util.*;

public class CollectionExample {

    private List<String> stringList;
    private Set<Integer> integerSet;
    private Map<String, Double> doubleMap;

    public CollectionExample() {
    }

    public void printCollections() {
        System.out.println("List:");
        for (String str : stringList) {
            System.out.println(str);
        }

        System.out.println("\nSet:");
        for (Integer num : integerSet) {
            System.out.println(num);
        }

        System.out.println("\nMap:");
        for (Map.Entry<String, Double> entry : doubleMap.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
    }

    public void setStringList(List<String> stringList) {
        this.stringList = stringList;
    }

    public void setIntegerSet(Set<Integer> integerSet) {
        this.integerSet = integerSet;
    }
    
    public void setDoubleMap(Map<String, Double> doubleMap) {
        this.doubleMap = doubleMap;
    }
}
bean 内部注入方式

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="collectionExample" class="com.zhumingjian.spring.entity.CollectionExample">
        <property name="stringList">
            <list>
                <value>Apple</value>
                <value>Orange</value>
                <value>Banana</value>
            </list>
        </property>
        <property name="integerSet">
            <set>
                <value>1</value>
                <value>2</value>
                <value>3</value>
            </set>
        </property>
        <property name="doubleMap">
            <map>
                <entry key="John" value="100.0"/>
                <entry key="Doe" value="200.0"/>
                <entry key="Jane" value="300.0"/>
            </map>
        </property>
    </bean>
</beans>

测试:

public static void main(String[] args) {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
	CollectionExample bean = context.getBean(CollectionExample.class);
	bean.printCollections();
}

输出结果:

List:
Apple
Orange
Banana

Set:
1
2
3

Map:
John -> 100.0
Doe -> 200.0
Jane -> 300.0
util 方式

使用 util 的方式需要额外引入命名空间,建议直接按照我下面这个来

<?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 https://www.springframework.org/schema/util/spring-util.xsd">
    <!-- 声明一个字符串的List -->
    <util:list id="stringList">
        <value>Apple</value>
        <value>Orange</value>
        <value>Banana</value>
    </util:list>
    <!-- 声明一个整数的Set -->
    <util:set id="integerSet">
        <value>1</value>
        <value>2</value>
        <value>3</value>
    </util:set>
    <!-- 声明一个键为字符串、值为双精度浮点数的Map -->
    <util:map id="doubleMap">
        <entry key="John" value="100.0"/>
        <entry key="Doe" value="200.0"/>
        <entry key="Jane" value="300.0"/>
    </util:map>
    <!-- 声明 CollectionExample 的 bean,并注入集合数据 -->
    <bean id="collectionExample" class="com.zhumingjian.spring.entity.CollectionExample">
        <property name="stringList" ref="stringList"/>
        <property name="integerSet" ref="integerSet"/>
        <property name="doubleMap" ref="doubleMap"/>
    </bean>
</beans>

测试:

public static void main(String[] args) {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
	CollectionExample bean = context.getBean(CollectionExample.class);
	bean.printCollections();
}

输出结果:

List:
Apple
Orange
Banana

Set:
1
2
3

Map:
John -> 100.0
Doe -> 200.0
Jane -> 300.0

P 命名空间

P命名空间是Spring提供的一种简化配置的方式,首先准备一个类:

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter 和 Setter 方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    // toString 方法,用于输出对象信息
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

要使用 P 命名空间,要引入特定的命名空间,最后的配置文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
           
    <!-- 使用P命名空间声明一个Person的bean,并注入name和age -->
    <bean id="person" class="Person"
          p:name="John"
          p:age="30" />

</beans>

测试:

public static void main(String[] args) {
	// 加载 Spring 配置文件
	ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
	// 获取 Person 对象
	Person person = (Person) context.getBean("person");
	// 输出 Person 对象信息
	System.out.println(person);
}

引入外部文件注入

Spring 允许你将外部的配置文件引入,以配置数据库连接池为例,首先准备一个外部的配置文件:

jdbc.user=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

Spring 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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:datasource.properties"/>
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

自动装配

在 Spring 框架中,自动装配(Autowiring)是一个强大的特性,它允许 Spring 容器在不需要显式指定依赖关系的情况下,自动将 bean 装配到一起。
在 Spring 的 XML 配置中,自动装配可以通过 bean 标签的 autowire 属性来实现。autowire 属性可以设置为以下几种模式之一:

  1. byName(按名称自动装配):Spring 容器查找与属性名相匹配的 bean 的 id。
  2. byType(按类型自动装配):Spring 容器查找与属性类型相匹配的 bean。如果存在多个相同类型的bean,则会抛出异常,因为它无法决定使用哪一个 bean。
  3. constructor(构造函数自动装配):类似于 byType,但用于构造函数参数。如果类有多个构造函数,且它们都是可接受的(即,具有相同数量的参数且每个参数的类型都匹配一个 bean),则可能会抛出异常。
  4. default(默认自动装配):这个值允许你指定自动装配的默认行为,但它本身并不启用自动装配。它实际上让子 beans 标签可以覆盖这个设置。
  5. no(不自动装配):这是默认设置,表示不进行自动装配。显式地设置这个值可以清晰地表明你不想使用自动装配。

以下是一个简单的配置例子:

<bean id="userInfoController" class="com.zhumingjian.spring.controller.UserInfoController" autowire="byType"/>
<bean id="userService" class="com.zhumingjian.spring.service.impl.UserServiceImpl"/>

bean 作用域

在 Spring 中可以通过配置 bean 标签的 scope 属性来指定 bean 的作用域范围,各取值含义参加下表:

取值含义创建对象的时机
singleton(默认)在 IOC 容器中,这个 bean 的对象始终为单实例IOC 容器初始化时
prototype这个 bean 在 IOC 容器中有多个实例获取 bean 时

如果是在 WebApplicationContext 环境下还会有另外几个作用域(但不常用):

取值含义
request在一个请求范围内有效
session在一个会话范围内有效

bean 生命周期

基本流程

  1. 实例化:当 Spring 容器接收到 bean 的定义后,通过构造函数创建 bean 的实例。
  2. 依赖注入:在实例化 bean 后,Spring 框架通过依赖注入将相关的依赖注入到 bean 中,包括属性注入、构造函数注入或方法注入。
  3. 调用后置处理器(初始化之前)
  4. 初始化方法调用:如果 bean 实现了 InitializingBean 接口,或者在配置文件中使用 init-method 指定了初始化方法,Spring 容器在依赖注入完成后调用这些方法进行一些初始化操作。
  5. 调用后置处理器(初始化之后)
  6. 使用:此时bean已经可以正常使用了,它会被注入到其他bean中,或者被容器中的其他组件使用。
  7. 销毁前回调方法:当 bean 不再需要时,Spring 容器会调用预定义的方法来释放资源,这些方法可以是实现 DisposableBean 接口的 destroy() 方法,或者在配置文件中使用 destroy-method 指定的方法。

后置处理器

bean 的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现 BeanPostProcessor 接口,且配置到 IoC 容器中,需要注意的是,bean 后置处理器不是单独针对某一个 bean 生效,而是针对 IoC 容器中所有bean 都会执行

创建 bean 的后置处理器:

public class MyBeanProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("☆☆☆" + beanName + " = " + bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("★★★" + beanName + " = " + bean);
        return bean;
    }
}

将后置处理器注册到容器中:

<!-- bean的后置处理器要放入IOC容器才能生效 -->
<bean id="myBeanProcessor" class="com.zhumingjian.spring.processor.MyBeanProcessor"/>

FactoryBean

当 Bean 的创建过程比较复杂时,可以通过 FactoryBean 来实现复杂的创建过程,隐藏实现细节,使使用者无需关心 Bean 的创建过程。

org.springframework.beans.factory.FactoryBean<T>

配置一个 FactoryBean 类型的 bean,在获取 bean 的时候得到的并不是 class 属性中配置的这个类的对象,而是 getObject() 方法的返回值。通过这种机制,Spring 可以帮我们把复杂组件创建的详细过程和繁琐细节都屏蔽起来,只把最简洁的使用界面展示给我们。

以下是一个简单的例子,首先编写一个 FactoryBean:

public class StudentFactoryBean implements FactoryBean<Student> {
    @Override
    public Student getObject() throws Exception {
        return new Student();
    }

    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }
}

然后将其注册到容器中:

<bean id="student" class="com.zhumingjian.spring.factory.StudentFactoryBean"/>

最后获取对象:

public static void main(String[] args) {
	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
	Student bean = context.getBean(Student.class);
}
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值