Spring Bean的生命周期及三级缓存流程

本文讲述了SpringBean从实例化到销毁的完整流程,涉及三级缓存、依赖注入和销毁策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring Bean 的生命周期说起来其实就三个大块:实例化Bean -> 设置属性(注入和装配) -> 初始化 -> 使用Bean -> 销毁Bean
这个很好理解,但是内部是怎么样注入,初始化以及销毁?经历怎么样的过程呢?追随这些问题来参考这篇文章。

我们先从 三级缓存 -> 实例化... 顺序逐步理解

一、三级缓存

一级缓存 (singletonObjects) :

        作用:存储完全初始化后的单例Bean对象。

        说明:当一个单例 Bean 被完全初始化之后会被放入到一级缓存中,并且确保其在容器中是唯一的。

二级缓存 (earlySingletonObjects) :

        作用:存储正在创建中的 Bean 对象,即存储正在创建中循环依赖的对象。

        说明:当一个循环依赖的对象在三级缓存中被检测到会进入到二级缓存中进行对象的创建。

三级缓存 (singletonFactories) :

        作用:用于存储 Bean 对象的创建工厂,即 Bean 在其中的工厂进行创建。

        说明:当对象创建好之后会进入 一级缓存。如果检测出存在循环依赖问题则会放入到二级缓存中进行创建。

理解了三级缓存,我们就清楚了初始化及完成后的 Bean 会存放在哪里了。


二、实例化(为 Bean 分配空间)

实例化在初始化之前,是需要给字节码中的这些对象分配空间出来,也就是为 Bean 分配内存空间。


三、设置属性 (Bean 注入和装配)

容器将所需要的属性值(依赖)注入到该 Bean 中的过程,这样 Bean 就可以在后续操作中使用这些属性了。

属性注入和装配的常见三种方式:

1.属性注入 

        使用 @Autowired、@Resource 配合 @Controller、@Service、@Repository、@Configuration、@Compoent 进行注入。

2.setter注入

        在设置 set 方法的上面加上 @Autowired 注解。

例:

@Controller
public class UserController {
    
    // 注入 Setter
    private User user;

    @Autowired
    public void setUser(User user) {
        this.user = user;
    }
}

3.构造器注入

        在构造方法中进行注入

例:

@Controller
public class UserController {

    // 构造方法注入
    private User user;

    @Autowired
    public UserController(User user) {
        this.user = user;
    }
}

当只有一个构造方法的时候可以省略 @Autowired。

经历这个阶段后 Bean 对象的属性会被注入进去。


四、初始化

  • 通过各种通知 Aware 方法,如 BeanNameAware 可以让对象在当前容器中的名称,BeanFactoryAware 可以获取工厂来获取其他对象,ApplicationContextAware 拥有比 BeanNameAware 更多的功能。
  • 执行 BeanPostProcessor 初始化前置方法,执行 postProcessBeforeInitialzation 方法。
  • 执行 PostConstruct 初始化方法,如果 Bean 上标记了 @PostConstruct 注解,那么在依赖注入完成后,会调用被标记的方法。这个注解表示该方法是在 Bean 初始化的最后阶段执行的,用于执行特定的初始化逻辑。
  • 执行自己指定的 init-method 方法,在配置 xml 文件中,这个方法会在依赖注入和 @PostConstruct 方法执行后被调用。
  • 执行 BeanPostProcessor 初始化后置方法,执行 postProcessAfterInitialization 方法。

代码实例:

BeanLifeComponent

//                        BeanLifeComponent 

package com.controller;

import com.Student.Student;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class BeanLifeComponent implements BeanNameAware, BeanFactoryAware, BeanPostProcessor {

    private String beanName;
    private BeanFactory beanFactory;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("执行了BeanNameAware的通知,这个方法可以让Bean知道容器的名称。 此时 bean 容器名称是 " + beanName);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println("执行了 setBeanFactory 获取到容器这样可以手动获取其他的Bean对象");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("执行了@PostConstruct初始化方法。");
    }

    public void init() {
        System.out.println("执行了xml的 init-method 指定初始化方法");
    }

    @PreDestroy
    public void preDestroy() {
        System.out.println("执行了PreDestory销毁方法");
    }


    public void doSomething() {
        Student student = beanFactory.getBean(Student.class);
        System.out.println("通过 beanFactory 获取到 student,信息为: " + student);
    }

}
Application 类:
//                                  Application
import com.controller.BeanLifeComponent;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Application {
    public static void main(String[] args) {
        // ClassPathXmlApplicationtext 是 ApplicaitonContext的子类,拥有销毁的方法
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");

        BeanLifeComponent beanLifeComponent = context.getBean("myComponent",BeanLifeComponent.class);

        System.out.println("使用Bean");
        // 销毁 Bean
        beanLifeComponent.doSomething();
        context.destroy();
        System.out.println("销毁了Bean");
    }
}

运行结果如下: 

执行了BeanNameAware的通知,这个方法可以让Bean知道容器的名称。 此时 bean 容器名称是 myComponent
执行了 setBeanFactory 获取到容器这样可以手动获取其他的Bean对象
执行了@PostConstruct初始化方法。
执行了xml的 init-method 指定初始化方法
使用Bean
通过 beanFactory 获取到 student,信息为: Student(name=张三, age=18)
执行了PreDestory销毁方法
销毁了Bean

Process finished with exit code 0

这个实例证明了上述的初始化过程。

接下来这个实例证明一下 @PostConstruct 方法和 @BeanPostProcessor 的顺序:

package com.Test;

import javafx.application.Application;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
class MyComponent {
    public MyComponent() {
        System.out.println("MyComponent constructor");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("MyComponent @PostConstruct");
    }
}

@Component
class MyBeanPostProcessor implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("Before Initialization: " + beanName);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("After Initialization: " + beanName);
        return bean;
    }
}

public class Main {
    public static void main(String[] args) {
//        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("spring-config.xml");
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        MyComponent myComponent = context.getBean(MyComponent.class);
        System.out.println("Main class: Bean retrieved");
    }
}

运行结果如下:

MyComponent constructor
Before Initialization: myComponent
MyComponent @PostConstruct
After Initialization: myComponent
Main class: Bean retrieved

Process finished with exit code 0

 


五、使用 Bean

正常使用 Bean 实现业务逻辑实现


六、销毁 Bean

销毁 Bean 是指在关闭容器的时候,Spring 会执行一些清理工作,以释放资源或者执行必要的关闭操作。如果不显式定义销毁方法可能不会释放比如数据库连接等资源。他只会尝试在容器中释放单例 Bean 的资源。但是对于原型(prototype)的 Bean ,Spring 不自动执行销毁方法,因为原型 Bean 的生命周期不受 Spring 容器管理。

一、@PreDestory注解

@Component
public class MyBean {
    @PreDestroy
    public void preDestroy() {
        // 执行销毁前的逻辑
    }
}

二、实现 DisposableBean 接口

@Component
public class MyBean implements DisposableBean {
    @Override
    public void destory() throws Exception {
        // 执行销毁方法
    }
}

三、在配置文件中指定 destory-method

在 XML 配置文件中,通过 destroy-method 属性来指定 Bean 的销毁方法。

<bean id="myBean" class="com.example.MyBean" destroy-method="customDestroyMethod" />

这时,需要在 MyBean 类中定义一个 customDestoryMethod 的方法。

这就是完整的 Bean 生命周期

<think>好的,用户问的是如何在路由器上安装和配置AList文件列表程序。首先我需要回忆一下AList的相关知识。AList是一个支持多种存储的文件列表程序,可以挂载网盘等,通常通过Docker部署。用户提到的是在路由器上安装,所以需要确认路由器是否支持Docker。 根据引用[1],之前有在ARMv7架构的玩客云主机上安装AList和Aria2的例子,使用的是Docker部署。路由器如果是基于类似架构,并且支持Docker的话,步骤可能类似。但很多普通家用路由器可能没有足够的性能或存储来运行Docker,所以需要用户确认路由器的系统是否支持,比如是否是OpenWrt这类第三方固件。 接下来,我需要整理安装步骤。首先,确保路由器系统兼容,比如OpenWrt,并且安装了Docker。然后通过SSH登录路由器,使用Docker命令拉取AList镜像,创建容器,配置端口和数据目录。之后进入AList的Web界面进行进一步设置,比如添加存储和用户管理。 另外,引用[3]提到有些步骤已有详细教程,可能需要引导用户查看相关链接。用户可能还需要内网穿透,如引用[1]中的Cpolar,来公网访问AList,但用户问题暂时没提到这部分,可能后续会需要。 需要注意检查路由器的资源是否足够,存储空间和内存。如果路由器无法运行Docker,可能需要交叉编译AList的二进制文件,或者寻找适合路由器架构的预编译版本,手动安装。 最后,生成相关问题,帮助用户深入解决可能遇到的问题,比如性能优化、结合Aria2使用,或者内网穿透配置。</think>### 如何在路由器上安装和配置AList文件列表程序? #### 前提条件 1. **路由器系统需支持Docker**:推荐使用OpenWrt等支持容器化部署的固件,并确保路由器存储空间和内存足够运行容器[^1]。 2. **SSH访问权限**:需能通过SSH登录路由器后台。 --- #### 分步操作指南 ##### 1. 安装Docker环境 - 若路由器系统未预装Docker,需通过包管理器手动安装。例如在OpenWrt中: ```bash opkg update opkg install docker docker-compose ``` ##### 2. 拉取AList镜像并启动容器 通过SSH连接路由器后执行: ```bash docker run -d \ --name alist \ -p 5244:5244 \ -v /mnt/sda1/alist:/opt/alist/data \ --restart=always \ xhofe/alist:latest ``` - **参数说明**: - `-p 5244:5244`:将容器内5244端口映射到路由器 - `-v /mnt/sda1/alist:/opt/alist/data`:挂载存储目录(需提前创建`/mnt/sda1/alist`) - `--restart=always`:设置容器自动重启 ##### 3. 获取管理员密码 执行以下命令查看初始密码: ```bash docker exec -it alist ./alist admin random ``` 或查看日志: ```bash docker logs alist | grep password ``` ##### 4. 访问Web界面 浏览器输入 `http://路由器IP:5244`,使用用户名`admin`和上一步获取的密码登录。 ##### 5. 配置存储与功能 - **添加存储**:在Web界面中选择“存储” > “添加”,支持阿里云盘、OneDrive等20+种存储类型。 - **用户管理**:可创建多用户并分配不同存储权限。 - **主题与插件**:支持自定义主题和扩展功能插件。 --- #### 常见问题解决 - **端口冲突**:若5244端口被占用,修改`-p`参数为其他端口(如`-p 8080:5244`)。 - **存储挂载失败**:检查挂载路径权限,执行`chmod -R 777 /mnt/sda1/alist`。 - **性能问题**:若路由器资源不足,可关闭非必要插件或限制容器内存: ```bash docker update --memory 256M alist ``` --- #### 扩展应用(结合引用[1]) 若需公网访问AList,可参考引用[1]通过**Cpolar内网穿透**生成公网地址,或配置DDNS实现固定域名访问。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

极品小學生

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

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

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

打赏作者

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

抵扣说明:

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

余额充值