• Spring容器&Bean生命周期常见接口


    1 Spring 容器的基本概念

    1.1 基本概念

    🔗 官方文档

    在 Spring 中,使用 org.springframework.context.ApplicationContext 接口代表Spring IoC 容器,负责实例化、配置和组装 bean。容器通过读取配置元数据来获取关于要实例化、配置和组装哪些对象的指令。

    在这里插入图片描述

    官方的解释:将你的业务对象通过配置数据元的形式(xml或java bean、注解)存储到 Spring 容器中,方便后续的使用。

    其中xml的形式,在 Spring 5的时候,已经很少使用了,基本都是 Java Bean 和 注解的形式来配置数据元。

    Java Bean 的形式:

    主要使用的注解是 @Configuration 、@Bean、@Component等

    注解的形式:

    主要使用的注解是 @Autowired、@Resource 等,它们在实现上,实际是通过 BeanPostProcessor 的接口实现来处理的。

    ⭐️ 特别提示:本文的核心内容,放在了 Spring IOC 容器上。

    2 将对象交给容器的几种方式

    • Xml 文件配置的方式,构造器、工厂。
    • Java Bean的配置方式。

    现在的 SpringBoot 项目中,使用最多的是:

    1. 从外部导入的属性值,比如定义一个 Bean,读取Nacos 的配置,进而生成一个对象。
    2. 在 Configuration 标注的类中,定义带有返回值的方法,加上 @Bean注解,有多个Bean或需要指定优先级,需要再增加 Order 或 Primary注解。
        @Primary
        @Bean
        public Cpu cpu() {
            return new Cpu("先进的CPU");
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3 从容器中获取对象

    简单的分为两种场景,第一种是和Spring框架耦合性较大的获取方式;第二种,是耦合度小的方式。

    3.1 通过指定的接口获取

    Spring 提供了 BeanFactory 接口,可以使用 BeanFactoryAware 接口获取该对象,然后在通过 BeanFactory 接口提供的获取Bean 的方法即可。

    比如:

    package org.feng.common;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.stereotype.Component;
    
    /**
     * BeanFactoryAware 实现类:可以获取到 BeanFactory 实例
     *
     * @version V1.0
     * @author: fengjinsong
     * @date: 2022年06月29日 14时03分
     */
    @Component
    @Slf4j
    public class BeanFactoryAwareImpl implements BeanFactoryAware {
    
        private static BeanFactory beanFactory;
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            log.info("使用 BeanFactoryAware 获取到 BeanFactory {}", beanFactory);
            BeanFactoryAwareImpl.beanFactory = beanFactory;
        }
    
        public static <T> T getBean(Class<T> clazz) {
            return beanFactory.getBean(clazz);
        }
    
        public static <T> T getBean(String name, Class<T> clazz) {
            return beanFactory.getBean(name, clazz);
        }
    
        public static Object getBean(String name) {
            return beanFactory.getBean(name);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    既然BeanFactory本身是可以这样操作,那么作为它的子接口,ApplicationContext 也能达到同样的相关,甚至ApplicationContext 的功能更多,因为它本身还继承了其他的接口。这个详细见本项目中的类:ApplicationContextAwareImpl

    另外,在Spring Bean 的生命周期中,也可以通过对应的接口获取Bean对象。

    一般这类操作,会用在初始化数据的场景上,业务场景用的较少。

    3.2 使用注解注入

    推荐使用这种方式。

    对应的注解中,使用最多的就是 @Resource@Autowired

    个人建议,使用Resource注解,具体原因见本文中的 【6 常见注解】 中的 6.12 小节。

    4 容器启动时 Bean 的生命周期

    在这里插入图片描述

    注意:上图只说明Spring中的单例Bean。不讨论多例和懒加载的情况。

    文字描述:

    首先,在启动Spring应用时,会通过扫描包及Class文件,对使用了特殊注解、实现了特定接口的类信息进行组装,获得对应的BeanDefinition。根据BeanDefinition,初始化对应的单例Bean对象。

    初始化的过程是,根据Java的反射获取到对应的构造器,调用构造器方法。然后注入属性、以及其他的一些初始化操作。

    5 常用接口

    5.1 ApplicationContextAware

    5.1.1 接口定义

    public interface ApplicationContextAware extends Aware {
        /**
         * 可以看到是 set 了 ApplicationContext 的实例
         */
        void setApplicationContext(ApplicationContext var1) throws BeansException;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    该接口用来获取 ApplicationContext 的实例,并使用它进行一些操作。

    5.2 ApplicationContext

    在这里插入图片描述

    继承关系图如上,本次主要涉及到 BeanFactory 的一些说明。

    5.3 InitializingBean

    public interface InitializingBean {
        void afterPropertiesSet() throws Exception;
    }
    
    • 1
    • 2
    • 3

    用于在对象Bean的生命周期中,自定义一些配置。

    5.4 BeanPostProcessor

    提供给使用者,用来在自定义配置之前或之后,执行一些操作。

    这个接口的实现类,可以有多个,但是只对单例,非懒加载的Bean对象有效。使用时需要额外注意。

    5.5 ApplicationListener

    暂不说明,有兴趣的朋友看看下面的文章:

    https://blog.csdn.net/liyantianmin/article/details/81017960

    5.6 BeanNameAware

    可以通过实现这个接口,来获取 beanName。

    5.7 BeanFactoryAware

    可以通过实现这个接口,来获取 BeanFactory。

    5.8 BeanDefinition

    本小节的内容,来自官网的翻译:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-basics

    Spring IoC 容器管理一个或多个 bean。这些 bean 是使用您提供给容器的配置元数据创建的(例如,以 XML <bean/>定义的形式)。在容器本身中,这些 bean 定义表示为BeanDefinition 对象,其中包含(以及其他信息)以下元数据:

    属性含义
    ClassInstantiating Beans类信息,一般是包名+类名
    NameNaming Beans别名
    ScopeBean Scopes创建对象的范围
    Constructor argumentsDependency Injection构造器的参数列表
    PropertiesDependency Injection依赖注入的属性
    Autowiring modeAutowiring Collaborators自动注入模式
    Lazy initialization modeLazy-initialized Beans懒加载
    Initialization methodInitialization Callbacks初始化方法回调
    Destruction methodDestruction Callbacks销毁方法回调

    6 常见注解

    6.1 @Component

    表示将一个类创建对象,注入到Spring中。不区分指定场景。

    6.2 @Controller

    控制器注解,在经典的分层中,最接近前端的位置。标注类和前端请求、响应相关联。

    有时也会和 Scope 搭配着使用,指定为原型模式。

    6.3 @Service

    业务处理层注解,在经典的分层中,用于业务实现层的实现类。将对应的对象注入容器中。

    6.4 @Repository

    仓储层的注解是,标志将指定类的对象注入到容器中。

    在经典分层的 DAO,数据访问层使用。一般和数据源打交道。

    6.5 @Configuration

    配置注解,在创建配置类的时候,需要使用这个注解。

    该注解中,一般会配置多个Bean。

    6.6 @Bean

    方法级的注解,用在带有返回值的方法上。功能上和老版的Spring 的 bean 标签是一样的。

    6.7 @Scope

    标注创建对象的范围。

    一般常用的是,singleton 和 prototype。

    Scope描述
    singleton(Spring 默认)将单个 bean 定义限定为每个 Spring IoC 容器的单个对象实例。在spring容器启动时,会将其初始化,并存到容器中。每次获得到的都是同一个对象。
    prototype将单个 bean 定义限定为任意数量的对象实例。只有在 getBean 的时候,才会创建对象、初始化。
    request将单个 bean 定义限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,该实例是在单个 bean 定义的后面创建的。仅在 Web 感知 Spring 的上下文中有效ApplicationContext。Url不发生变化,就是同一个对象。
    session将单个 bean 定义限定为 HTTP 的生命周期Session。仅在 Web 感知 Spring 的上下文中有效ApplicationContext。当前会话未发生改变(一般是浏览器未关闭)
    application将单个 bean 定义限定为ServletContext. 仅在 Web 感知 Spring 的上下文中有效ApplicationContext
    websocket将单个 bean 定义限定为WebSocket. 仅在 Web 感知 Spring 的上下文中有效ApplicationContext

    6.8 @Order

    同样的功能的还有 Ordered 接口。

    它们会控制Bean的执行顺序,也就是在注入时,具体注入的是哪个对象。

    指定的数值越小,表示优先级越高。

    6.9 @Lazy

    标注对象的初始化为懒加载。延迟加载单例Bean,减少容器启动时的时间。在第一次使用该Bean时,会创建并初始化。

    6.10 @Autowired

    根据属性类型进行自动装配。一般建议使用Resource注解。

    关于它,官方的几个例子是不错的:

    https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-autowired-annotation

    6.11 @Qualifier

    指定需要查找并注入的bean 的 beanName。与@Autowired一起使用

    6.12 @Resource

    https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-resource-annotation

    关于 Resource 的官方说明,较少,以下是本人的一些认识:

    可以根据类型或指定名称进行属性对象注入。

    不指定 name 时,按照类型注入。比如:以下的使用,需要有一个数据类型为 Cpu 的bean。容器中找到多个同类型的bean时,会报错。

        @Resource
        private Cpu cpu;
    
    • 1
    • 2

    当Spring容器中存在多个同类型bean,会报错:意思是,期待找到一个bean,但是找到了多个。因此不知道该注入哪一个,此时需要指定名称。

    No qualifying bean of type 'org.feng.entity.Cpu' available: expected single matching bean but found 3: cpu,oldCpu,cpuWithBeanName
    
    • 1

    当指定name是,会按照 name 去容器中找对应的bean。如果通过名字找不到对应的bean,会直接报找不到对应名称的bean。

    该注解是javax中的一个注解。而Autowired是 Spring框架中的注解。

    6.13 @Value

    从配置文件中获取属性值。

    在 application.properties 中配置:

    # 应用名称
    spring.application.name=spring-learn
    # 应用服务 WEB 访问端口
    server.port=8080
    
    computer.server1=sssssssssss
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用时,在对应的类中:

    // 当没有配置对应属性,使用冒号,后面写上属性的默认值
    @Value("${computer.server:北京某地}")
    private String server;
    // 从配置中获取对应的属性值
    @Value("${computer.server1}")
    private String server1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6.14 @PostConstruct

    初始化,属于生命周期的一部分,在Bean创建后,自定义初始化的逻辑。

    6.15 @PreDestroy

    销毁前,属于生命周期的一部分。在Bean销毁时执行指定逻辑,一般用于释放资源。

    附录

    1 创建SpringBoot项目

    1.1 设置加速创建项目的地址

    https://start.aliyun.com/
    在这里插入图片描述

    1.2 填写项目信息

    在这里插入图片描述

    这一步填写好信息之后,就直接点击【Next】。

    1.3 选择依赖

    本次练习,只涉及到 Spring 本身,为了方便调试,使用 Spring Web 的依赖。

    在这里插入图片描述

    选择之后,点击 【Finish】即可。

    Attention:注意整个过程中,网络连接一定保持正常!还有就是事先配置好Maven环境。

    1.4 调整创建好的项目目录&文件

    在这里插入图片描述

    首先删除选择中的目录及文件。

    然后在 pom文件中调整 Java 的版本,调整为 11版本。

    本次不使用测试包,因此同时删除测试目录以及依赖。随后增加 Lombok 依赖备用。

    目录结构就变成了以下的模样:

    在这里插入图片描述

    1.5 调整后的 pom 文件内容

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.feng</groupId>
        <artifactId>spring-learn</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>spring-learn</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>11</java.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <spring-boot.version>2.4.1</spring-boot.version>
            <lombok.version>1.18.22</lombok.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
                <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>${lombok.version}</version>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                    <configuration>
                        <source>11</source>
                        <target>11</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.4.1</version>
                    <configuration>
                        <mainClass>org.feng.SpringLearnApplication</mainClass>
                    </configuration>
                    <executions>
                        <execution>
                            <id>repackage</id>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    1.6 启动项目

    运行 SpringLearnApplication 中的 main 方法即可。

    1.7 项目下载

    Gitee 仓库地址如下:

    https://gitee.com/fengsoshuai/spring-learn

    2 简易版的Spring实现

    实现了Spring中的一些注解,接口。从扫描包,到BeanFactory,Bean的初始化等。

    博客地址:

    https://blog.csdn.net/FBB360JAVA/article/details/124437455

    Gitee 仓库:

    https://gitee.com/fengsoshuai/demo-spring

  • 相关阅读:
    Golang MQTT的使用 实现发布订阅
    Claude 2,它有 GPT-4 一些无法超越的能力
    带有ttl的Lru在Rust中的实现及源码解析
    Python 文件读写操作区别案例(r、r+、rb、w、w+、wb、a、a+、ab)
    使用 cephadm 部署 aio ceph
    Java进阶学习笔记15——接口概述
    Iphone文件传到电脑用什么软件,看这里
    跨域问题,无法得到数据,啾啾我把
    小白版:如何运行vue项目(后端+前端)
    ipv6检测易语言代码
  • 原文地址:https://blog.csdn.net/FBB360JAVA/article/details/125547477