Spring框架中Bean的生命周期:源码解析与最佳实践

第1章:Spring Bean生命周期概述

1.1 什么是Spring Bean生命周期?

定义:Spring Bean生命周期是指从Bean的创建、初始化、使用到销毁的完整过程,由Spring容器严格管理 。核心思想是Spring容器通过IoC(控制反转)和DI(依赖注入)机制,控制Bean的创建、配置和销毁,开发者可通过扩展点(如BeanPostProcessorAware接口)插入自定义逻辑。

Spring管理的Bean生命周期与普通Java对象有本质区别。普通Java对象由程序员直接new创建,其生命周期完全由JVM垃圾回收机制管理,开发者无法精确控制初始化和销毁时机。而Spring容器管理的Bean,其生命周期的每个关键节点都有明确的控制机制,开发者可以指定初始化方法和销毁方法,或者通过实现特定接口来干预Bean的创建过程。

关键区别:Spring容器管理的Bean具有明确的创建和销毁阶段,且在这些阶段提供了多个扩展点,允许开发者在特定时机执行自定义逻辑,例如资源初始化、权限校验、日志记录等。

1.2 为什么需要了解Bean生命周期?

必要性

  • 在正确时机执行关键操作:如初始化数据库连接池、加载缓存、配置资源等,这些操作通常需要在Bean完全初始化后执行。
  • 避免隐式错误:了解生命周期有助于避免在构造函数中访问未注入的依赖,防止NullPointerException
  • 理解扩展点机制:掌握BeanPostProcessor、Aware接口等扩展点的调用顺序,有助于正确编写自定义逻辑。
  • 解决常见问题:如循环依赖、资源泄漏、初始化失败等问题,都需要基于对生命周期的理解来解决。

实际案例

  • 资源泄漏:如果未在@PostConstruct中关闭数据库连接,可能导致资源泄漏。
  • 循环依赖:在单例Bean中使用@Autowired注入相互依赖的Bean时,Spring通过三级缓存机制解决循环依赖问题。
  • 作用域管理:在Web应用中,request作用域的Bean需要在请求结束时销毁,而session作用域的Bean需要在会话结束时销毁。
1.3 Spring容器的角色

IoC容器:Spring通过BeanFactory(基础容器)和ApplicationContext(应用上下文,功能更丰富)管理Bean的生命周期。DI机制(依赖注入)是IoC的核心体现,它将对象的创建和依赖关系的管理交给Spring容器,而非由程序员显式控制。

容器类型对比

容器类型特点适用场景
BeanFactory轻量级,仅提供基础IoC功能非Web环境,资源受限场景
ApplicationContext功能丰富,支持国际化、事件发布、AOP等Web应用,需要完整Spring功能的场景

源码片段:Spring容器的核心是AbstractApplicationContext类,其refresh()方法负责容器的完整初始化流程:

// AbstractApplicationContext.java
public void refresh() throws BeansException,恶性代码

总结:Spring容器通过分层抽象(BeanFactory、ApplicationContext)和核心类(如DefaultListableBeanFactory)实现Bean生命周期管理,开发者可以通过扩展点(如BeanPostProcessor)在特定时机插入自定义逻辑。

第2章:生命周期的核心阶段详解

2.1 实例化(Instantiation)

定义:Spring容器通过反射或工厂方法创建Bean的原始对象(未填充属性)。关键点包括:

  • 支持多种实例化方式
    • 无参构造函数
    • 带参构造函数
    • 工厂方法(静态或实例方法)
  • 作用域影响
    • 单例(Singleton)Bean在容器启动时实例化
    • 原型(Prototype)Bean在每次调用getBean()时实例化
    • Web作用域(Request/Session)Bean在请求或会话开始时实例化

代码示例

// 使用无参构造函数实例化
@Component
public class UserService {
    public UserService() {
        System.out.println("1. 实例化UserService");
    }
}

// 使用工厂方法实例化
@Component
public class UserRepository {
    public static UserRepository create() {
        System.out.println("1. 工厂方法创建UserRepository");
        return new UserRepository();
    }
}

源码解析:在AbstractAutowireCapableBeanFactory类中,doCreateBean()方法负责Bean的完整创建流程,其中createBeanInstance()方法负责实例化:

// AbstractAutowireCapableBeanFactory.java
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // 确定实例化策略(无参构造、带参构造或工厂方法)
    if (mbd.getFactoryBeanName() != null) {
        // 工厂方法实例化
        return EagerInitializationPolicy.eagerlyInitializeBean(
                mbd, beanName, args, getBeanFactory());
    }

    // 无参构造或带参构造实例化
    return BeanUtils.instantiateClass(mbd ResolvedBeanClass, args, getBeanFactory().getBeanClassLoader());
}

面试考点

  • 构造函数注入与字段注入的区别?
  • 为什么单例Bean在容器启动时实例化?
  • 如何自定义Bean的实例化方式?
2.2 属性赋值(Populate Properties)

定义:为Bean的字段或方法注入依赖对象(如@Autowired@Value)。关键点包括:

  • 依赖注入方式
    • 构造器注入(推荐,避免循环依赖)
    • 字段注入(通过@Autowired注解)
    • Setter方法注入(通过@Autowired或XML配置)
  • 属性赋值时机
    • 在实例化完成后、初始化方法执行前
    • 对于循环依赖,通过三级缓存机制提前暴露半成品Bean
  • 支持嵌套Bean的递归注入:Spring能够处理Bean之间的复杂依赖关系

代码示例

// 使用字段注入
@Component
public class UserRepository {
    @Value("${database.url}")
    private String url;
}

// 使用Setter方法注入
@Component
public class UserService {
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
        System.out.println("2. 字段注入UserRepository");
    }
}

源码解析:在AbstractAutowireCapableBeanFactory类中,populateBean()方法负责属性赋值:

// AbstractAutowireCapableBeanFactory.java
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // 解析属性值并注入
    PropertyValues pvs = (mbd.getResolvedLazyInit() ? new LazyPropertyValues() : mbd.getPropertyValues());
    applyPropertyValues(beanName, mbd, bw, pvs);

    // 处理循环依赖
    if (mbd.isSingleton() && this beanName != null && !mbd.isLazyInit()) {
        // 将半成品Bean放入三级缓存
        Object earlySingleton = getEarlySingletonReference(beanName, mbd, bw);
        // 将半成品Bean放入二级缓存
        this.earlySingletonObjects.put IfAbsent (beanName, earlySingleton);
        // 将半成品Bean放入三级缓存
        this singletonFactories.put IfAbsent (beanName, new ObjectFactory () {
            @Override
            public Object.getObject() {
                return getSingleton beanName;
            }
        });
    }
}

常见问题

  • 循环依赖:当两个Bean互相依赖时,Spring如何解决?
  • 依赖缺失:如果依赖的Bean尚未实例化,会抛出NoSuchBeanDefinitionException
  • 属性赋值失败:如果属性类型不匹配或值无法解析,会抛出BeanInitializationException
2.3 初始化前处理(BeanPostProcessor Before)

定义:在Bean初始化方法(如@PostConstructInitializingBean.afterPropertiesSet()或自定义init-method之前,Spring容器调用所有BeanPostProcessorpostProcessBeforeInitialization()方法,允许开发者修改Bean属性或状态 。

关键点

  • 全局性:对容器中的所有Bean生效
  • 执行顺序:由BeanPostProcessor的实现类决定,可通过实现PriorityOrderedOrdered接口控制执行顺序 
  • 可修改Bean:返回值可以替换原始Bean(如AOP代理)
  • 可阻断初始化:通过返回null阻断后续初始化流程

代码示例

// 自定义BeanPostProcessor
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("3.1 [" + beanName + "] 初始化前处理");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("3.2 [" + beanName + "] 初始化后处理");
        return bean;
    }

    @Override
    public int gettingOrder() {
        return HIGHEST_PRECEDENCE + 1; // 最高优先级
    }
}

源码解析:在AbstractAutowireCapableBeanFactory类中,initializeBean()方法负责初始化流程,其中包含对BeanPostProcessor的调用:

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
    // 调用Aware接口
    if (mbd.getSingleton() != null) {
        // 调用Aware接口方法
        applyAwareInterfaces(bean, mbd);
    }

    // 调用BeanPostProcessor的postProcessBeforeInitialization
    boolean continueWithInitialization = true;
    if (mbd postProcessed) {
        continueWithInitialization = applyBeanPostProcessorsBeforeInitialization (bean, beanName);
    }

    if (continueWithInitialization) {
        // 执行初始化方法
        invokingInitMethods (bean, beanName, mbd);
    }

    // 调用BeanPostProcessor的postProcessAfterInitialization
    return applyBeanPostProcessorsAfterInitialization (bean, beanName);
}

面试考点

  • BeanPostProcessor的执行顺序?
  • postProcessBeforeInitialization和postProcessAfterInitialization的区别?
  • BeanPostProcessor如何实现AOP代理?
2.4 初始化(Initialization)

定义:执行Bean的初始化方法,包括以下三种方式:

  1. 注解方式

    • @PostConstruct:标注在方法上,方法执行在属性赋值之后、BeanPostProcessor的postProcessAfterInitialization()之前
    • @PreDestroy:标注在方法上,方法执行在Bean销毁之前
  2. 接口方式

    • InitializingBean:实现afterPropertiesSet()方法
    • DisposableBean:实现destroy()方法
  3. 配置方式

    • XML配置中的init-method属性
    • Java配置中的@Bean(initMethod="...")

关键点

  • 执行顺序@PostConstruct > InitializingBean > init-method 
  • 单例Bean:初始化在容器启动时完成
  • 非单例Bean:初始化在每次获取Bean时完成

代码示例

// 使用多种初始化方式
@Component
public class UserService implements InitializingBean {
    @Value("${system.version}")
    private String version;

    @PostConstruct
    public void start() {
        System.out.println("4.1 @PostConstruct方法执行");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("4.2 InitializingBean接口方法执行");
    }

    @Bean(initMethod = "init")
    public void init() {
        System.out.println("4.3 init-method配置方法执行");
    }
}

源码解析:在AbstractAutowireCapableBeanFactory类中,invokingInitMethods()方法负责执行初始化方法:

// AbstractAutowireCapableBeanFactory.java
private void invokingInitMethods(String beanName, Object bean, RootBeanDefinition mbd) {
    // 调用初始化方法
    if (mbd.getInitMethod() != null) {
        // 调用自定义init-method
        invoker invoker = new invoker (mbd.getInitMethod(), bean.getClass());
        invoker invoker (bean);
    }

    // 调用InitializingBean接口方法
    if (bean instanceof initializingBean) {
        ((initializingBean) bean).afterPropertiesSet();
    }

    // 调用@PostConstruct注解方法
    if (mbd.getPostConstructMethod() != null) {
        invoker invoker = new invoker (mbd.getPostConstructMethod(), bean.getClass());
        invoker invoker (bean);
    }
}

常见问题

  • 初始化失败:如果初始化方法抛出异常,Bean将无法使用
  • 循环依赖:在初始化阶段处理循环依赖
  • 作用域限制:非单例Bean的初始化方法在每次获取Bean时执行
2.5 初始化后处理(BeanPostProcessor After)

定义:在Bean初始化方法执行完毕后,Spring容器调用所有BeanPostProcessorpostProcessAfterInitialization()方法,允许开发者对Bean进行最终修改或增强 。

关键点

  • AOP代理生成:Spring AOP通过postProcessAfterInitialization()方法为Bean生成代理对象
  • 全局性:对容器中的所有Bean生效
  • 可修改Bean:返回值可以替换原始Bean(如动态代理)
  • 可阻断使用:通过返回null阻断Bean的使用

代码示例

// AOP代理示例
@Component
public class AuditProxyPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof UserService) {
            // 创建代理对象
            return Proxy.newProxyInstance(
                    bean.getClass().getClassLoader(),
                    bean.getClass().getInterfaces(),
                    (proxy, method, args) -> {
                        System.out.println("5.1 [" + beanName + "] AOP代理方法执行");
                        return method.invoke bean, args;
                    }
            );
        }
        return bean;
    }
}

源码解析:在AbstractAutowireCapableBeanFactory类中,applyBeanPostProcessorsAfterInitialization()方法负责调用BeanPostProcessorpostProcessAfterInitialization()

// AbstractAutowireCapableBeanFactory.java
protected Object applyBeanPostProcessorsAfterInitialization(
        Object existingBean, String beanName) throws BeansException {
    // 遍历所有BeanPostProcessor
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 调用postProcessAfterInitialization方法
        Object current = processor.postProcessAfterInitialization(existingBean, beanName);
        if (current == null) {
            return existingBean;
        }
        existingBean = current;
    }
    return existingBean;
}

面试考点

  • BeanPostProcessor的执行顺序?
  • postProcessBeforeInitialization和postProcessAfterInitialization的区别?
  • 如何通过BeanPostProcessor实现AOP代理?
2.6 注册销毁回调(Destruction Callbacks)

定义:在Bean初始化完成后,Spring容器注册Bean的销毁回调方法,包括以下三种方式:

  1. 注解方式

    • @PreDestroy:标注在方法上,方法执行在Bean销毁之前
  2. 接口方式

    • DisposableBean:实现destroy()方法
  3. 配置方式

    • XML配置中的destroy-method属性
    • Java配置中的@Bean(destroyMethod="...")

关键点

  • 单例Bean:销毁回调在容器关闭时触发
  • 非单例Bean:销毁回调通常由开发者手动触发
  • 执行顺序@PreDestroy > DisposableBean > destroy-method 

代码示例

// 使用多种销毁方式
@Component
public class DatabasePool implements DisposableBean {
    @PreDestroy
    public void close() {
        System.out.println("6.1 @PreDestroy方法执行");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("6.2 DisposableBean接口方法执行");
    }

    @Bean(destroyMethod = "shutdown")
    public void shutdown() {
        System.out.println("6.3 destroy-method配置方法执行");
    }
}

源码解析:在AbstractBeanFactory类中,registerDestructionCallback()方法负责注册销毁回调:

// AbstractBeanFactory.java
public void registerDestructionCallback(String beanName, Runnable callback) {
    // 将销毁回调放入销毁回调Map
    this.destructionCallbacks.put IfAbsent (beanName, callback);
}

// AbstractApplicationContext.java
public void close() throws恶性代码 {
    // 遍历所有Bean并执行销毁方法
    for (String beanName : getBeanNamesForType(DisposableBean.class)) {
        DisposableBean disposableBean = (DisposableBean) getBean beanName;
        disposableBean.destroy();
    }

    // 执行自定义destroy-method
    for (String beanName : getBeanNamesForType(Object.class)) {
        BeanDefinition beanDefinition = getBeanDefinition beanName;
        if (beanDefinition.getDestroyMethod() != null) {
            invoker invoker = new invoker (beanDefinition.getDestroyMethod(), getBeanClass beanName);
            invoker invoker (getBean beanName);
        }
    }

    // 执行@PreDestroy注解方法
    for (String beanName : getBeanNamesForType(Object.class)) {
        if (isPreDestroyMethod beanName)) {
            invoker invoker = new invoker (getPreDestroyMethod beanName), getBeanClass beanName);
            invoker invoker (getBean beanName);
        }
    }
}

常见问题

  • 资源泄漏:如果未正确注册销毁回调,可能导致资源泄漏
  • 作用域限制:非单例Bean的销毁回调需要手动触发
  • 执行顺序:销毁方法的执行顺序与创建顺序相反
2.7 销毁(Destruction)

定义:当Spring容器关闭时(如调用close()方法或服务器停止),容器调用已注册的销毁方法,释放Bean占用的资源。

关键点

  • 单例Bean:销毁在容器关闭时自动执行
  • 非单例Bean:销毁需要手动触发或通过作用域代理实现
  • 执行顺序:与创建顺序相反,先创建的Bean后销毁

代码示例

// 手动关闭容器
public class App {
    public static void main(String[] args) {
        // 创建Spring容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        // 获取Bean并使用
        UserService userService = context.getBean(UserService.class);
        userService.saveUser();

        // 关闭容器,触发销毁方法
        context.close();
    }
}

源码解析:在AbstractApplicationContext类中,close()方法负责容器关闭和Bean销毁:

// AbstractApplicationContext.java
public void close() throws恶性代码 {
    // 执行销毁回调
    executeDestructionCallbacks();

    // 销毁单例Bean
    destroySingletons();

    // 销毁其他资源
    doClose();
}

// 执行销毁回调
private void executeDestructionCallbacks() {
    // 遍历所有Bean并执行销毁方法
    for (String beanName : getBeanNamesForType(Object.class)) {
        // 执行销毁方法
        destroyBean beanName, getBean beanName);
    }
}

面试考点

  • 单例Bean和原型Bean的销毁机制有何不同?
  • @PreDestroy和 disposableBean接口的执行顺序?
  • 如何确保非单例Bean的销毁方法被调用?

第3章:生命周期的扩展点与自定义

3.1 BeanPostProcessor的深度解析

定义BeanPostProcessor是Spring框架提供的一个扩展接口,允许开发者在Bean初始化的前后插入自定义逻辑 。

接口定义

// BeanPostProcessor.java
public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

关键子接口

  • InstantiationAwareBeanPostProcessor:允许在Bean实例化前后进行干预
  • InitializationAwareBeanPostProcessor:允许在Bean初始化前后进行干预
  • DestructionAwareBeanPostProcessor:允许在Bean销毁前后进行干预

执行顺序:Spring容器在调用BeanPostProcessor方法时,遵循以下顺序:

  1. 实现PriorityOrdered接口的处理器(按优先级排序)
  2. 实现Ordered接口的处理器(按顺序排序)
  3. 未实现排序接口的处理器(按注册顺序排序)

代码示例:自定义BeanPostProcessor实现日志增强

// 自定义BeanPostProcessor
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("[" + beanName + "] 初始化前处理");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("[" + beanName + "] 初始化后处理");
        return bean;
    }

    @Override
    public int gettingOrder() {
        return HIGHEST_PRECEDENCE + 1; // 最高优先级
    }
}

源码解析:在PostProcessorRegistrationDelegate类中,sortPostProcessors()方法负责排序BeanPostProcessor:

// PostProcessorRegistrationDelegate.java
private static List<BeanPostProcessor> sortPostProcessors(
        List<BeanPostProcessor> postProcessors) {
    // 创建排序器
    BeanPostProcessorSorter处理器排序器 = new BeanPostProcessorSorter();

    // 按优先级排序
   处理器排序器.sortByPriority postProcessors);

    // 按顺序排序
   处理器排序器.sortByOrder postProcessors);

    // 返回排序后的列表
    return处理器排序器.getSortedPostProcessors();
}

应用场景

  • AOP代理:通过postProcessAfterInitialization()为Bean生成代理对象
  • 日志记录:在Bean方法调用前后添加日志
  • 性能监控:在Bean方法调用前后记录执行时间
  • 安全控制:在Bean方法调用前进行权限校验

面试考点

  • BeanPostProcessor的执行顺序?
  • postProcessBeforeInitialization和postProcessAfterInitialization的区别?
  • 如何通过BeanPostProcessor实现AOP代理?
3.2 BeanFactoryPostProcessor的作用与使用场景

定义BeanFactoryPostProcessor是Spring框架提供的一个扩展接口,允许开发者在Bean实例化之前修改Bean的定义(BeanDefinition) 。

接口定义

// BeanFactoryPostProcessor.java
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws恶性代码;
}

关键子接口

  • BeanDefinitionRegistryPostProcessor:允许在Bean定义加载阶段修改BeanDefinition

执行时机:在Bean定义加载完成后、Bean实例化之前执行 ,与BeanPostProcessor的执行阶段不同。

代码示例:修改Bean的属性值

// 自定义BeanFactoryPostProcessor
@Component
public class PropertyModifyingPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 获取特定Bean的定义
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
        // 修改Bean的属性值
        beanDefinition.setPropertyValues(new PropertyValues(
                new PropertyValue("version", "6.0.11")
        ));
    }
}

源码解析:在AbstractApplicationContext类中,refresh()方法负责调用BeanFactoryPostProcessor:

// AbstractApplicationContext.java
public void refresh() throws恶性代码 {
    // ...

    // 执行BeanFactoryPostProcessor
    invokerBeanFactoryPostProcessors getBeanFactory());

    // ...

    // 执行BeanPostProcessor
    invokerBeanPostProcessors getBeanFactory());

    // ...
}

应用场景

  • 动态修改Bean定义:如修改Bean的属性值、作用域等
  • 扫描特定注解:如自动扫描并注册MyBatis的Mapper接口
  • 条件化装配:根据环境条件决定是否注册某些Bean

面试考点

  • BeanFactoryPostProcessor和BeanPostProcessor的区别?
  • BeanDefinitionRegistryPostProcessor的作用?
  • 如何通过BeanFactoryPostProcessor修改Bean的属性?
3.3 @PostConstruct与@PreDestroy的底层实现

定义@PostConstruct@PreDestroy是Spring框架提供的注解,用于在Bean的初始化和销毁阶段执行特定方法 。

注解定义

// PostConstruct.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PostConstruct {
}

// PreDestroy.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PreDestroy {
}

底层实现:Spring通过CommonAnnotationBeanPostProcessor处理这些注解 :

// CommonAnnotationBeanPostProcessor.java
public class CommonAnnotationBeanPostProcessor extends AbstractBeanPostProcessor
        implements BeanNameAware, BeanFactoryAware, InitializeBean, DisposableBean {
    // 处理PostConstruct注解
    private void processPostConstructMethods(Object bean) {
        // 获取所有PostConstruct注解方法
        Method[] methods = getPostConstructMethods bean.getClass());
        for (Method method : methods) {
            // 执行方法
            invoker method.invoke bean);
        }
    }

    // 处理PreDestroy注解
    private void processPreDestroyMethods(Object bean) {
        // 获取所有PreDestroy注解方法
        Method[] methods = getPreDestroyMethods bean.getClass());
        for (Method method : methods) {
            // 注册销毁回调
            registerDestructionCallback getBeanName(), () -> {
                method.invoke bean);
            });
        }
    }
}

执行顺序:在Bean生命周期中,@PostConstruct方法的执行顺序在InitializingBean.afterPropertiesSet()之后、自定义init-method之前。

代码示例

// 使用@PostConstruct和@PreDestroy
@Component
public class CacheService {
    @PostConstruct
    public void initCache() {
        System.out.println("缓存初始化完成");
    }

    @PreDestroy
    public void clearCache() {
        System.out.println("缓存清理完成");
    }
}

面试考点

  • @PostConstruct和InitializingBean接口的执行顺序?
  • @PreDestroy和 disposableBean接口的执行顺序?
  • @PostConstruct和@PreDestroy在Spring 6.x中的行为有何变化?
3.4 InitializingBean与DisposableBean接口的适用性对比

对比表格

特性InitializingBean接口disposableBean接口@PostConstruct注解@PreDestroy注解init-method/destroy-method配置
实现方式实现接口实现接口使用注解使用注解在配置文件中指定
执行顺序在属性赋值之后、BeanPostProcessor之前在BeanPostProcessor之后在BeanPostProcessor之后在BeanPostProcessor之前在BeanPostProcessor之后
适用场景需要访问其他Bean的场景需要访问其他Bean的场景简单初始化逻辑简单销毁逻辑需要明确指定方法名的场景
灵活性有限,只能使用固定方法名有限,只能使用固定方法名高,可以标注在任何方法上高,可以标注在任何方法上中等,需要在配置中指定方法名
代码侵入性高,需要实现接口高,需要实现接口低,只需要添加注解低,只需要添加注解低,只需要在配置中指定方法名

源码解析:在AbstractAutowireCapableBeanFactory类中,initializeBean()方法负责执行初始化方法:

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
    // ...

    // 调用Aware接口
    applyAwareInterfaces(bean, mbd);

    // 调用BeanPostProcessor的postProcessBeforeInitialization
    applyBeanPostProcessorsBeforeInitialization (bean, beanName);

    // 执行初始化方法
    invokingInitMethods (bean, beanName, mbd);

    // 调用BeanPostProcessor的postProcessAfterInitialization
    return applyBeanPostProcessorsAfterInitialization (bean, beanName);
}

最佳实践

  • 优先使用注解:如@PostConstruct@PreDestroy,因为它们代码侵入性低
  • 避免循环依赖:在初始化方法中不要依赖其他正在初始化的Bean
  • 处理异常:在初始化和销毁方法中捕获并处理异常

面试考点

  • @PostConstruct和InitializingBean接口的执行顺序?
  • @PreDestroy和 disposableBean接口的执行顺序?
  • 如何解决循环依赖问题?
3.5 自定义初始化/销毁方法(XML与注解配置)

XML配置方式

<!-- spring.xml -->
<bean id="databasePool" class="com.example.DatabasePool"
      init-method="init" destroy-method="destroy">
    <property name="url" value="${database.url}"/>
    <property name="username" value="${database.username}"/>
    <property name="password" value="${database.password}"/>
</bean>

注解配置方式

// 使用@Bean注解指定初始化和销毁方法
@Configuration
public class AppConfig {
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public DatabasePool databasePool() {
        return new DatabasePool();
    }
}

代码示例:自定义初始化和销毁方法

// 自定义Bean
public class DatabasePool {
    private String url;
    private String username;
    private String password;

    // 初始化方法
    public void init() {
        System.out.println("数据库连接池初始化完成");
    }

    // 销毁方法
    public void destroy() {
        System.out.println("数据库连接池清理完成");
    }

    // Getter和Setter方法
    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String得到用户名() {
        return username;
    }

    public void设置用户名(String username) {
        this.username = username;
    }

    public String得到密码() {
        return password;
    }

    public void设置密码(String password) {
        this.password = password;
    }
}

源码解析:在AbstractBeanFactory类中,registerBeanDefinition()方法负责注册Bean的定义,包括初始化和销毁方法:

// AbstractBeanFactory.java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws恶性代码 {
    // 检查Bean名称是否重复
    checkBeanName beanName);

    // 检查Bean是否已经注册
    check是否存在BeanDefinition beanName);

    // 注册BeanDefinition
    this beanDefinitionMap.put beanName, beanDefinition);

    // 如果是单例Bean,注册销毁回调
    if (beanDefinition.getScope().equals(Scope.SCOPE-singleton)) {
        // 注册销毁回调
        registerDestructionCallback beanName, () -> {
            if (beanDefinition.getDestroyMethod() != null) {
                invoker invoker = new invoker (beanDefinition.getDestroyMethod(), getBeanClass beanName);
                invoker invoker (getBean beanName);
            }
        });
    }
}

应用场景

  • 资源初始化:如数据库连接池、文件句柄等
  • 状态加载:如从文件或数据库加载配置
  • 监控配置:如初始化监控系统

面试考点

  • XML配置和注解配置销毁方法的区别?
  • 如何确保自定义销毁方法被调用?
  • 单例Bean和原型Bean的销毁方法执行条件?

第4章:Bean作用域与生命周期管理

4.1 单例(Singleton)作用域的生命周期控制

定义:单例作用域是Spring的默认作用域,每个Spring容器中只会创建一个Bean实例,该实例在整个应用程序生命周期内共享。

关键点

  • 实例化时机:容器启动时或首次获取Bean时(取决于是否延迟初始化)
  • 属性赋值时机:实例化后立即进行
  • 初始化时机:属性赋值完成后立即进行
  • 销毁时机:容器关闭时自动执行销毁方法 

代码示例

// 单例Bean配置
@Component
public class UserService {
    @Autowired
    private UserRepository userRepository;

    @PostConstruct
    public void init() {
        System.out.println("单例Bean初始化完成");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("单例Bean销毁完成");
    }
}

源码解析:在DefaultSingletonBeanRegistry类中,get Singleton()方法负责获取单例Bean:

// DefaultSingletonBeanRegistry.java
public Object get Singleton(String beanName, boolean allowEarlyReference) {
    // 从一级缓存获取
    Object singletonObject = this singletonObjects.get beanName;
    if (singletonObject == null) {
        // 从二级缓存获取
        singletonObject = this earlySingletonObjects.get beanName;
        if (singletonObject == null && allowEarlyReference) {
            // 从三级缓存获取
            ObjectFactory<?> singletonFactory = (ObjectFactory<?>) this singletonFactories.get beanName;
            if (singletonFactory != null) {
                // 创建半成品Bean
                singletonObject = singletonFactory.getObject();
                // 将半成品Bean放入二级缓存
                this.earlySingletonObjects.put beanName, singletonObject;
                // 将半成品Bean放入三级缓存
                this singletonFactories.remove beanName);
            }
        }
    }
    return singletonObject;
}

面试考点

  • 单例Bean的销毁时机?
  • 如何确保单例Bean的线程安全性?
  • 单例Bean的循环依赖如何解决?
4.2 原型(Prototype)作用域的生命周期限制

定义:原型作用域表示每次请求Bean时,Spring容器都会创建一个新的Bean实例,这些实例之间是完全独立的。

关键点

  • 实例化时机:每次调用getBean()方法时
  • 属性赋值时机:实例化后立即进行
  • 初始化时机:属性赋值完成后立即进行
  • 销毁时机:Spring容器不管理,需要手动触发销毁方法 

代码示例

// 原型Bean配置
@Component
@Scope("prototype")
public class prototypedBean {
    @PostConstruct
    public void init() {
        System.out.println("原型Bean初始化完成");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("原型Bean销毁完成");
    }
}

源码解析:在AbstractBeanFactory类中,getBean()方法负责获取Bean实例:

// AbstractBeanFactory.java
public <T> T getBean(String name, Class<T> requiredType) {
    // 如果是原型Bean,直接创建新实例
    if (getBeanDefinition(name).getScope().equals(Scope.SCOPE-prototype)) {
        return createBean(name, getBeanDefinition(name), null);
    }

    // 否则,获取单例Bean
    return (T) getSingleton(name);
}

销毁方法触发方式

  1. 手动调用:在使用完原型Bean后,手动调用其销毁方法
  2. 作用域代理:通过@Scope(proxyMode=TargetClass)启用代理,使原型Bean支持销毁方法 
  3. 第三方工具:如使用对象池管理原型Bean,由对象池自动触发销毁

面试考点

  • 原型Bean的销毁机制?
  • 如何确保原型Bean的销毁方法被调用?
  • 原型Bean和单例Bean的循环依赖如何解决?
4.3 Web作用域(Request/Session)的特殊处理

定义:Web作用域是Spring为Web应用提供的特殊作用域,包括request、session和global session。

关键点

  • request作用域:每个HTTP请求创建一个Bean实例,请求结束后自动销毁 
  • session作用域:每个HTTP会话创建一个Bean实例,会话过期后自动销毁 
  • global session作用域:仅适用于Portlet应用,基于全局会话创建Bean实例 

配置方式:在Web应用中,需要配置相应的监听器或过滤器来支持Web作用域 :

<!-- web.xml -->
< listener >
    < listener-class > org.springframework.web.context.request.RequestContextListener </ listener-class >
</ listener >
< listener >
    < listener-class > org.springframework.web.context.session.SessionContextListener </ listener-class >
</ listener >

代码示例:request作用域Bean

// request作用域Bean
@Component
@Scope("request")
public class RequestScopeBean {
    @PostConstruct
    public void init() {
        System.out.println("request作用域Bean初始化完成");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("request作用域Bean销毁完成");
    }
}

源码解析:在RequestScope类中,get()方法负责获取request作用域Bean:

// RequestScope.java
public Object get(String name, ObjectFactory<?> objectFactory) {
    // 获取当前HTTP请求
    RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
    // 从请求属性中获取Bean
    Object scopedObject = attributes.getAttribute(name, getScope());
    if (scopedObject == null) {
        // 创建新Bean
        scopedObject = objectFactory.getObject();
        // 将Bean放入请求属性
        attributes.setAttribute(name, scopedObject, getScope());
    }
    return scopedObject;
}

销毁机制

  1. request作用域:通过RequestContextListener监听HTTP请求结束,自动销毁Bean 
  2. session作用域:通过SessionContextListener监听HTTP会话过期,自动销毁Bean 

面试考点

  • Web作用域Bean的销毁机制?
  • 如何确保Web作用域Bean的销毁方法被调用?
  • request作用域和session作用域的区别?

第5章:源码级解析与常见问题

5.1 核心类与方法(BeanDefinition、BeanFactory等)

BeanDefinition:Spring框架中表示Bean配置的元数据,包含Bean的类名、属性值、依赖关系等信息 。

关键类

  • RootBeanDefinition:表示一个完整的Bean定义,包含所有配置信息
  • BeanDefinitionReader:负责读取Bean定义的配置文件或注解
  • BeanDefinitionRegistry:管理Bean定义的注册表,允许动态注册和修改Bean定义

BeanFactory:Spring的IoC容器接口,负责管理Bean的生命周期 。

关键类

  • DefaultListableBeanFactory:Spring的默认BeanFactory实现,提供了完整的Bean管理功能
  • AbstractAutowireCapableBeanFactory:负责Bean的实例化、属性赋值和初始化的抽象类
  • AbstractApplicationContext:Spring的应用上下文实现,提供了完整的容器管理功能

源码解析:在AbstractAutowireCapableBeanFactory类中,doCreateBean()方法负责Bean的完整创建流程:

// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // 实例化Bean
    BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
    Object bean = instanceWrapper.getWrappedInstance();

    // 属性赋值
    populateBean(beanName, mbd, instanceWrapper);

    // 初始化
    exposedObject = initializeBean(beanName, exposedObject, mbd);

    // 注册销毁回调
    registerDestructionCallback(beanName, () -> {
        if (exposedObject instanceof DisposableBean) {
            ((DisposableBean) exposedObject).destroy();
        }
        if (mbd.getDestroyMethod() != null) {
            invoker invoker = new invoker (mbd.getDestroyMethod(), exposedObject.getClass());
            invoker invoker (exposedObject);
        }
    });

    return exposedObject;
}

面试考点

  • BeanDefinition的作用?
  • BeanFactory和ApplicationContext的区别?
  • doCreateBean()方法的执行流程?
5.2 循环依赖的解决机制与生命周期影响

定义:循环依赖是指两个或多个Bean相互依赖形成闭环的情况,是Spring中一个经典问题 。

解决机制:Spring通过三级缓存(singletonObjectsearlySingletonObjectssingletonFactories)解决循环依赖问题。

三级缓存作用

  • singletonObjects(一级缓存):存储完全初始化的单例Bean
  • earlySingletonObjects(二级缓存):存储提前暴露的半成品Bean(已完成实例化但未完成初始化)
  • singletonFactories(三级缓存):存储Bean工厂,用于生成早期Bean引用

源码解析:在AbstractAutowireCapableBeanFactory类中,getEarlyBeanReference()方法负责处理循环依赖:

// AbstractAutowireCapableBeanFactory.java
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, BeanWrapper instanceWrapper) {
    // 获取半成品Bean
    Object bean = instanceWrapper.getWrappedInstance();

    // 如果需要AOP代理
    if (mbd.isProxy()) {
        // 创建代理对象
        bean = getAopProxy(mbd, instanceWrapper, beanName).getProxy();
    }

    // 将半成品Bean放入二级缓存
    this.earlySingletonObjects.put IfAbsent (beanName, bean);

    // 将半成品Bean放入三级缓存
    this singletonFactories.put IfAbsent (beanName, new ObjectFactory () {
        @Override
        public Object getObject() {
            return getSingleton beanName;
        }
    });

    return bean;
}

生命周期影响

  • 实例化阶段:提前将半成品Bean放入三级缓存
  • 属性赋值阶段:通过三级缓存获取半成品Bean引用
  • 初始化阶段:延迟执行初始化方法,直到所有依赖注入完成

面试考点

  • Spring如何解决循环依赖问题?
  • 三级缓存的具体作用?
  • 构造器注入的循环依赖能否解决?
5.3 资源泄漏的排查与解决(如未关闭数据库连接)

定义:资源泄漏是指Bean在销毁时未正确释放资源,导致资源(如数据库连接、文件句柄等)无法回收。

常见场景

  • 数据库连接泄漏:未在@PreDestroy中关闭数据库连接
  • 文件句柄泄漏:未在@PreDestroy中关闭文件流
  • 网络连接泄漏:未在@PreDestroy中关闭网络连接

排查工具

  1. YourKit Java Profiler:实时监控内存使用情况,创建多个堆快照进行对比分析 
  2. Eclipse Memory Analyzer (MAT):分析堆转储文件,找出未释放的资源
  3. lsof命令:检查进程打开的文件句柄数 

解决方案

  1. 确保销毁方法被调用:对于单例Bean,确保容器正常关闭;对于原型Bean,手动调用销毁方法或启用作用域代理 
  2. 使用try-with-resources:确保资源在使用后自动关闭 
  3. 实现 DisposableBean接口:在destroy()方法中释放资源 
  4. 使用@PreDestroy注解:在方法上添加注解,确保资源释放 

代码示例:资源泄漏修复

// 修复前:资源未关闭
@Component
public class DatabaseService {
    private Connection connection;

    @PostConstruct
    public void init() {
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @PreDestroy
    public void destroy() {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

面试考点

  • 如何排查资源泄漏?
  • 如何确保资源在Bean销毁时释放?
  • 单例Bean和原型Bean的资源管理有何不同?
5.4 初始化失败的调试技巧(日志分析与断点调试)

定义:初始化失败是指Bean在初始化过程中抛出异常,导致Bean无法使用。

常见原因

  • 依赖缺失:依赖的Bean尚未创建
  • 属性值错误:属性值无法解析或类型不匹配
  • 循环依赖:两个Bean互相依赖,无法完成初始化
  • 配置错误:XML或注解配置错误
  • 资源访问失败:如无法连接数据库

调试技巧

  1. 日志分析:启用Spring的DEBUG级别日志,查看Bean初始化的详细过程
  2. 断点调试:在IDE中设置断点,跟踪Bean初始化的执行流程
  3. 异常堆栈分析:查看抛出的异常信息,定位具体问题
  4. 使用@DependsOn:明确指定Bean的依赖顺序
  5. 使用@Lazy:延迟初始化Bean,避免循环依赖

源码解析:在AbstractAutowireCapableBeanFactory类中,initializeBean()方法负责执行初始化方法,如果抛出异常,会记录日志并抛出BeanInitializationException

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
    // ...

    try {
        // 执行初始化方法
        invokingInitMethods (bean, beanName, mbd);
    } catch (恶性代码 ex) {
        // 记录异常
        String msg = "Initialization of bean failed";
        throw new恶性代码 (msg, ex);
    }

    // ...
}

面试考点

  • 如何调试Bean初始化失败的问题?
  • 如何查看Spring容器的初始化日志?
  • 如何解决BeanCurrentlyInCreationException异常?

第6章:实际开发中的最佳实践

6.1 如何选择初始化/销毁方法的实现方式

选择建议

  • 优先使用注解:如@PostConstruct@PreDestroy,因为它们代码侵入性低
  • 避免实现接口:如InitializingBeanDisposableBean,因为它们代码侵入性高
  • 慎用init-method/destroy-method:仅在无法使用注解时使用

适用场景

  • 注解方式:简单初始化逻辑,如加载配置、初始化资源等
  • 接口方式:需要访问其他Bean的场景,如依赖注入后的复杂初始化
  • 配置方式:需要明确指定方法名的场景,如第三方库的Bean

代码示例:不同方式的使用场景

// 注解方式:简单初始化
@Component
public class CacheService {
    @PostConstruct
    public void initCache() {
        System.out.println("缓存初始化完成");
    }

    @PreDestroy
    public void clearCache() {
        System.out.println("缓存清理完成");
    }
}

// 接口方式:需要访问其他Bean
@Component
public class UserService implements InitializingBean, DisposableBean {
    @Autowired
    private UserRepository userRepository;

    @Override
    public void afterPropertiesSet() throws恶性代码 {
        // 使用其他Bean
        userRepository.loadUsers();
        System.out.println("UserService初始化完成");
    }

    @Override
    public void destroy() throws恶性代码 {
        // 使用其他Bean
        userRepository.close();
        System.out.println("UserService销毁完成");
    }
}

// 配置方式:第三方库的Bean
@Configuration
public class AppConfig {
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public ThirdPartyService thirdPartyService() {
        return new ThirdPartyService();
    }
}

面试考点

  • @PostConstruct和InitializingBean接口的执行顺序?
  • @PreDestroy和 disposableBean接口的执行顺序?
  • 如何解决循环依赖问题?
6.2 避免在构造函数中访问未注入的依赖

问题描述:在构造函数中访问未注入的依赖会导致NullPointerExceptionBeanCurrentlyInCreationException异常 。

原因分析:依赖注入在构造函数之后进行,因此在构造函数中无法访问通过依赖注入的字段。

解决方案

  1. 避免在构造函数中访问依赖:将依赖访问逻辑移到初始化方法中
  2. 使用构造器注入:通过@Autowired注解在构造函数参数上,确保依赖在实例化时注入
  3. 使用@PostConstruct方法:在初始化方法中访问依赖
  4. 使用Aware接口:如ApplicationContextAware,在setApplicationContext()方法中访问依赖

代码示例:正确使用构造器注入

// 正确做法:使用构造器注入
@Component
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @PostConstruct
    public void init() {
        // 在初始化方法中访问依赖
        userRepository.loadUsers();
    }
}

// 错误做法:在构造函数中访问依赖
@Component
public class UserService {
    private UserRepository userRepository;

    public UserService() {
        // 此时 userRepository尚未注入
        userRepository.loadUsers(); // 抛出NullPointerException
    }

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

面试考点

  • 为什么在构造函数中访问依赖会导致问题?
  • 如何正确使用构造器注入?
  • 如何避免BeanCurrentlyInCreationException异常?
6.3 高性能场景下的延迟初始化(Lazy-init)应用

定义:延迟初始化是指Bean的初始化方法在首次获取Bean时执行,而不是在容器启动时执行。

关键点

  • 减少启动时间:对于启动时不需要的Bean,可以延迟初始化
  • 避免循环依赖:在某些情况下,延迟初始化可以避免循环依赖
  • 适用场景:大型应用、需要按需加载的Bean

配置方式

  1. XML配置<bean lazy-init="true">
  2. 注解配置@Bean(lazyInit = true)
  3. 全局配置<context:annotation-config lazy-init="true"/>

代码示例:延迟初始化配置

// 使用延迟初始化
@Configuration
public class AppConfig {
    @Bean(lazyInit = true)
    public ExpensiveService expensiveService() {
        System.out.println("ExpensiveService实例化");
        return new ExpensiveService();
    }

    @Bean
    public UserService userService() {
        System.out.println("UserService实例化");
        return new UserService();
    }
}

输出顺序

UserService实例化
ExpensiveService实例化(当首次获取ExpensiveService时)

面试考点

  • 延迟初始化对Bean生命周期的影响?
  • 如何配置延迟初始化?
  • 延迟初始化和@Lazy注解的区别?
6.4 多模块项目中的Bean生命周期管理策略

问题描述:在多模块项目中,Bean的生命周期管理需要考虑模块之间的依赖关系和作用域。

解决方案

  1. 模块化配置:将每个模块的Bean配置放在独立的配置类中
  2. 作用域管理:根据模块需求选择合适的作用域
  3. 依赖管理:使用@Import@ComponentScan明确指定模块之间的依赖关系
  4. 初始化顺序控制:使用@DependsOn@Order控制Bean的初始化顺序
  5. 销毁顺序控制:使用@Order控制Bean的销毁顺序

代码示例:多模块配置

// 模块A配置
@Configuration
public class ModuleAConfig {
    @Bean
    public ServiceA serviceA() {
        return new ServiceA();
    }
}

// 模块B配置
@Configuration
@Import(ModuleAConfig.class)
public class ModuleBConfig {
    @Bean
    public ServiceB serviceB() {
        return new ServiceB();
    }

    @Bean
    public ServiceC serviceC() {
        return new ServiceC();
    }
}

// 模块C配置
@Configuration
@Import({ModuleAConfig.class, ModuleBConfig.class})
public class ModuleCConfig {
    @Bean
    public ServiceD serviceD() {
        return new ServiceD();
    }
}

面试考点

  • 如何管理多模块项目中的Bean生命周期?
  • 如何控制Bean的初始化顺序?
  • 如何确保跨模块的Bean能够正确销毁?

第7章:高级特性与应用场景

7.1 基于条件的Bean生命周期控制

定义:根据特定条件(如环境变量、操作系统、Java版本等)决定是否创建和销毁Bean。

实现方式

  1. @Conditional注解:根据条件决定是否创建Bean
  2. 环境变量检查:在初始化方法中检查环境变量
  3. 属性文件配置:通过属性文件决定Bean的创建和销毁

代码示例:条件化Bean创建

// 使用@Conditional注解
@Configuration
public class AppConfig {
    @Bean
    @Conditional"OnLinux")
    public LinuxService linuxService() {
        return new LinuxService();
    }

    @Bean
    @Conditional"OnWindows")
    public WindowsService windowsService() {
        return new WindowsService();
    }
}

// 条件判断器
public class OnLinux implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment(). propertyNames(). contains("os.name=Linux");
    }
}

public class OnWindows implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return context.getEnvironment(). propertyNames(). contains("os.name=Windows");
    }
}

面试考点

  • @Conditional注解的作用?
  • 如何根据环境变量控制Bean的生命周期?
  • 如何实现跨平台的Bean生命周期控制?
7.2 基于事件的Bean生命周期控制

定义:通过Spring的事件机制,在特定事件发生时触发Bean的初始化或销毁。

关键类

  • ApplicationEvent:表示一个应用事件
  • ApplicationListener:监听应用事件
  • EventDrivenBean:实现事件驱动的Bean

应用场景

  • 缓存预热:在容器启动后,监听ContextRefreshedEvent事件,触发缓存预热
  • 资源释放:在容器关闭前,监听ContextClosedEvent事件,触发资源释放
  • 按需加载:在特定业务事件发生时,动态创建和销毁Bean

代码示例:事件驱动的缓存预热

// 缓存预热监听器
@Component
public class CachePreheater implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 缓存预热
        System.out.println("缓存预热开始");
        // 加载数据到缓存
        // ...
        System.out.println("缓存预热完成");
    }
}

// 资源释放监听器
@Component
public class ResourceReleaser implements ApplicationListener<ContextClosedEvent> {
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        // 资源释放
        System.out.println("资源释放开始");
        // 关闭数据库连接等
        // ...
        System.out.println("资源释放完成");
    }
}

面试考点

  • Spring事件机制如何工作?
  • 如何监听容器启动和关闭事件?
  • 如何实现基于事件的Bean生命周期控制?
7.3 基于作用域代理的Bean生命周期管理

定义:作用域代理允许非单例作用域的Bean(如原型、request、session)支持销毁方法,通过代理对象管理Bean的生命周期。

关键点

  • 代理模式:使用ProxyMode.TARGETProxyMode.动静态创建代理对象
  • 支持销毁方法:通过代理对象触发销毁方法
  • 适用场景:需要在Bean销毁时执行特定操作的非单例Bean

配置方式

// 启用作用域代理
@Component
@Scope(value = "prototype", proxyMode = ProxyMode.TARGET)
public class prototypeBean {
    @PostConstruct
    public void init() {
        System.out.println("原型Bean初始化完成");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("原型Bean销毁完成");
    }
}

源码解析:在ScopeProxyFactory类中,createProxy()方法负责创建作用域代理:

// ScopeProxyFactory.java
public Object createProxy(String beanName, Object target, BeanFactory beanFactory) {
    // 创建代理对象
    return ProxyFactory.getProxy(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new invokerHandler (beanName, target, beanFactory)
    );
}

// invokerHandler.java
public class invokerHandler implements MethodInterceptor {
    private String beanName;
    private Object target;
    private BeanFactory beanFactory;

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) {
        // 拦截方法调用
        if (method.getName().equals("destroy")) {
            // 触发销毁方法
            destroyBean beanName, target);
            return null;
        }

        // 调用目标方法
        return methodProxy.invoke(target, args);
    }

    private void destroyBean(String beanName, Object target) {
        // 从容器中移除Bean
        beanFactory.destroyBean beanName, target);
    }
}

面试考点

  • 作用域代理如何工作?
  • 如何为原型Bean启用销毁方法?
  • 作用域代理对Bean性能的影响?
7.4 基于作用域的Bean生命周期管理

定义:根据不同作用域(如单例、原型、request、session)管理Bean的生命周期。

关键策略

  1. 单例Bean:使用@PreDestroy disposableBean接口,确保容器关闭时释放资源
  2. 原型Bean:使用作用域代理,或手动调用销毁方法
  3. request作用域Bean:使用RequestContextListener,确保请求结束后释放资源
  4. session作用域Bean:使用SessionContextListener,确保会话过期后释放资源

代码示例:不同作用域的Bean配置

// 单例Bean
@Component
public class SingletonBean {
    @PostConstruct
    public void init() {
        System.out.println("单例Bean初始化完成");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("单例Bean销毁完成");
    }
}

// 原型Bean(启用作用域代理)
@Component
@Scope(value = "prototype", proxyMode = ProxyMode.TARGET)
public class prototypeBean {
    @PostConstruct
    public void init() {
        System.out.println("原型Bean初始化完成");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("原型Bean销毁完成");
    }
}

// request作用域Bean
@Component
@Scope("request")
public class RequestScopeBean {
    @PostConstruct
    public void init() {
        System.out.println("request作用域Bean初始化完成");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("request作用域Bean销毁完成");
    }
}

面试考点

  • 不同作用域Bean的生命周期有何不同?
  • 如何确保非单例Bean的销毁方法被调用?
  • Web作用域Bean的销毁机制?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

探索java

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

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

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

打赏作者

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

抵扣说明:

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

余额充值