首页 > 编程语言 >Springboot 的Bean生命周期五步、七步、十步详解以及框架源码解读生命周期-面试热点-xunznux

Springboot 的Bean生命周期五步、七步、十步详解以及框架源码解读生命周期-面试热点-xunznux

时间:2024-07-25 22:24:48浏览次数:20  
标签:初始化 生命周期 Spring bean 七步 Bean 源码 方法

文章目录

Springboot 的Bean生命周期五步、七步、十步详解以及框架源码解读生命周期

之前的文章有对Springboot 启动时Bean的创建与注入这个过程的讲解以及对应的源码解读,感兴趣的可以去看看:
Springboot 启动时Bean的创建与注入(一)-源码解读-xunznux
Springboot 启动时Bean的创建与注入(二)-源码解读-xunznux

为什么要知道Bean的生命周期

Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。

注解说明
@Component该注解用于描述 Spring 中的 Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如 Service 层、Dao 层等。 使用时只需将该注解标注在相应类上即可。
@Repository该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Service该注解通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
@Controller该注解通常作用在控制层(如SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。

其实生命周期的本质是:在哪个时间节点上调用了哪个类的哪个方法。
我们需要充分的了解在这个生命线上,都有哪些特殊的时间节点。
只有我们知道了特殊的时间节点都在哪,到时我们才可以确定代码写到哪。
我们可能需要在某个特殊的时间点上执行一段特定的代码,这段代码就可以放到这个节点上。当生命线走到这里的时候,自然会被调用。

了解Bean的生命周期对Spring框架的使用和管理有重要意义,主要原因包括以下几个方面:

  1. 确保Bean的正确初始化和销毁
    • 初始化:在Bean实例化后,Spring会对Bean进行初始化操作,如依赖注入、调用初始化方法等。了解生命周期可以确保在适当的时机执行这些操作,避免未初始化的Bean被使用。
    • 销毁:在容器关闭时,Spring会调用Bean的销毁方法。了解生命周期可以确保在销毁前执行必要的清理操作,释放资源,避免内存泄漏。
  2. 控制Bean的创建和依赖注入
    • 依赖注入:了解生命周期可以确保在Bean创建过程中正确地注入依赖对象,避免依赖关系不完整导致的错误。
    • 懒加载:通过控制生命周期,可以实现懒加载(Lazy Initialization),即在需要时才创建Bean,提升应用性能。
  3. 管理Bean的作用域
    • 单例(Singleton)和原型(Prototype):不同作用域的Bean有不同的生命周期管理方式。了解生命周期有助于正确使用和管理不同作用域的Bean,确保它们在合适的时机被创建和销毁。
    • 其他作用域:如请求(Request)、会话(Session)等作用域的Bean也有各自的生命周期管理需求。
  4. 支持AOP和其他框架功能
    • AOP(面向切面编程):Spring的AOP功能在Bean的不同生命周期阶段(如初始化前后、方法调用前后)插入额外的逻辑。了解生命周期可以更好地利用AOP功能,实现横切关注点(如日志记录、事务管理等)。
    • 事件监听:Spring提供了生命周期事件(如ContextRefreshedEvent、ContextClosedEvent等)。了解生命周期可以在合适的时机监听和处理这些事件。
  5. 调试和故障排除
    • 调试:了解生命周期可以帮助开发人员在调试时确定问题发生的具体阶段,快速定位和解决问题。
    • 故障排除:在应用出现问题时,了解生命周期可以帮助识别是哪个阶段出现了问题,如Bean未正确初始化、依赖注入失败等。
  6. 自定义生命周期行为
    • 自定义初始化和销毁方法:了解生命周期可以让开发人员自定义Bean的初始化和销毁方法,以满足特定需求。
    • Bean后处理器(BeanPostProcessor):Spring允许开发人员通过BeanPostProcessor接口在Bean的初始化和销毁前后添加自定义逻辑。了解生命周期可以有效地利用这些接口,实现自定义行为。
  7. 性能优化
    • 资源管理:通过了解生命周期,可以在合适的时机管理和优化资源使用,如连接池、线程池等,提升应用性能。
    • 延迟初始化:避免在应用启动时初始化所有Bean,减少启动时间,提升性能。

总结:
了解Bean的生命周期不仅是使用Spring框架的基础,也是有效管理和优化应用的重要手段。通过深入理解Bean的创建、初始化、销毁等各个阶段的细节,开发人员可以更好地控制Bean的行为,确保应用的稳定性、性能和可维护性。

Bean 的生命周期之五步

参考源码:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

/**
 * 实际创建指定的bean。在此之前,已进行了预创建处理,例如检查{@code postProcessBeforeInstantiation}回调。
 * <p>区分默认的bean实例化、使用工厂方法和自动装配构造器。
 *
 * @param beanName bean的名称
 * @param mbd 合并后的bean定义
 * @param args 构造函数或工厂方法调用时使用的显式参数
 * @return bean的新实例
 * @throws BeanCreationException 如果无法创建bean
 * @see #instantiateBean
 * @see #instantiateUsingFactoryMethod
 * @see #autowireConstructor
 */
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
       throws BeanCreationException {

    // 1. 实例化(Instantiation)
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
       // 从缓存中尝试获取单例bean的实例包装器,以支持原型作用域内的单例工厂bean。
       instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
       // 如果没有从缓存中获取到,调用方法创建新的bean实例。
       instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
       // 记录解析后的目标类型,用于后续依赖查找。
       mbd.resolvedTargetType = beanType;
    }

    // 2. 依赖注入与Bean定义后处理(Dependency Injection & Bean Definition Post Processing)
    // 允许后处理器修改合并的bean定义。
    synchronized (mbd.postProcessingLock) {
       if (!mbd.postProcessed) {
          try {
             applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
          }
          catch (Throwable ex) {
             throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                   "Post-processing of merged bean definition failed", ex);
          }
          // 标记bean定义已完成后处理。
          mbd.markAsPostProcessed();
       }
    }

    // 对于单例bean,且允许循环引用的情况下,提前暴露bean,以便解决潜在的循环引用问题。
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
          isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
       if (logger.isTraceEnabled()) {
          logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
       }
       // 添加一个单例工厂,用于提前获取bean的引用。
       addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // 3. 初始化(Initialization)
    // 初始化bean实例。
    Object exposedObject = bean;
    try {
       // 填充bean属性。
       populateBean(beanName, mbd, instanceWrapper);
       // 执行初始化方法,获取最终的bean实例。
       exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
       // 处理初始化过程中抛出的异常。
       if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
          throw bce;
       }
       else {
          throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
       }
    }
    
    // 如果提前暴露了单例,则进行相应的处理,以确保依赖的bean使用正确的bean实例。
    // 4. 处理提前暴露的对象,使用 Bean
    if (earlySingletonExposure) {
       // 获取最终的单例引用。
       Object earlySingletonReference = getSingleton(beanName, false);
       if (earlySingletonReference != null) {
          // 如果当前bean就是最终的bean,则直接使用提前暴露的引用。
          if (exposedObject == bean) {
             exposedObject = earlySingletonReference;
          }
          // 检查是否有依赖于原始bean的其他bean,确保它们能使用最终版本的bean。
          else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
             String[] dependentBeans = getDependentBeans(beanName);
             Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
             for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                   actualDependentBeans.add(dependentBean);
                }
             }
             if (!actualDependentBeans.isEmpty()) {
                throw new BeanCurrentlyInCreationException(beanName,
                      "Bean with name '" + beanName + "' has been injected into other beans [" +
                      StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                      "] in its raw version as part of a circular reference, but has eventually been " +
                      "wrapped. This means that said other beans do not use the final version of the " +
                      "bean. This is often the result of over-eager type matching - consider using " +
                      "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
             }
          }
       }
    }

    // 5. 注册销毁方法(Register Destruction Method)
    // 注册bean的销毁方法,以便在容器关闭时执行。
    try {
       registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
       throw new BeanCreationException(
             mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }
    
    // 返回最终的bean实例。
    return exposedObject;
}

这段代码是Spring框架内部用于创建和初始化Bean的核心逻辑。我们可以将其总结为Bean生命周期的五个主要步骤,这与Spring Bean生命周期的理论模型相吻合。以下是根据代码总结的五个步骤:

  1. 实例化(Instantiation)(Bean的无参构造方法执行)
    实际创建 Bean 实例。检查 Bean 是否是单例,是否需要缓存,是否使用工厂方法。通过 createBeanInstance 方法创建 Bean 实例,可能使用构造函数或工厂方法创建Bean实例,并存储在BeanWrapper中。
  2. 依赖注入(Dependency Injection)&提前暴露
    在实例化后,Spring会调用applyMergedBeanDefinitionPostProcessors,允许Bean后处理器修改Bean定义(即Bean属性赋值)。这一步还包括解决依赖关系,即通过setter方法或构造器注入其他Bean作为依赖。
    通过提前缓存单例 Bean,确保可以在 Bean 初始化过程中解决依赖问题。如果 Bean 是单例并且允许循环引用,则调用 addSingletonFactory 方法缓存早期 Bean 引用。
  3. 初始化(Initialization)
    调用populateBean方法,完成剩余的属性填充。
    接着调用initializeBean方法,执行Bean的初始化逻辑,包括调用任何自定义的初始化方法(如果配置了init-method)。(会调用Bean的init方法,这个 init 方法需要自己写自己配,方法名随意,需要在xml的bean中配置 init-method)
    例子:
@RestController
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/user")
    public UserDO getUser(String name) {
        return userService.getUserByName(name);
    }
}

对于这个,首先是创建 UserController 的 Bean,然后在 doCreateBean 方法的 populateBean 部分,会在某一步调用 autowireResource 它在 Spring 框架的上下文中用于自动装配资源对象。此方法的主要功能是根据给定的工厂 (BeanFactory) 和元数据 (LookupElement) 自动查找并返回一个资源对象。autowireResource 会调用方法resolveDependency ,其目的是解析和满足由 DependencyDescriptor 描述的依赖关系。接着调用 doResolveDependency ,这个函数是Spring框架中用于解析依赖项的函数,它根据提供的DependencyDescriptor对象,解析所需的依赖项并返回相应的对象。接着是 resolveCandidate 方法,它会调用 beanFactory.getBean(beanName);//beanName=userServiceImpl。接着就进入了 userServiceImpl Bean 的创建过程。

  1. 处理提前暴露的对象(使用Bean)
    因为提前暴露是在初始化前进行的,所以如果之前存在早期暴露的问题,则需要处理提前暴露的对象,指向初始化后的最终对象。
    如果 Bean 是单例且允许循环引用,则处理早期暴露的情况,确保引用指向最终的 Bean 实例。确保循环引用问题得到解决,即使在 Bean 初始化过程中可能存在的早期暴露问题。如果 Bean 是单例并且已经提前缓存,则处理早期暴露的情况,确保其他依赖 Bean 获取到最终的 Bean 实例。
  2. 注册销毁方法(Register Destruction Method)
    如果Bean定义中配置了destroy-method,则注册一个销毁回调,确保在Bean不再需要时可以执行清理操作。
    这些步骤确保了Bean的正确创建、配置和清理,是Spring框架管理Bean生命周期的基础。需要注意的是,实际的Bean生命周期可能会因为特定的配置和后处理器而有所不同,但上述步骤涵盖了大部分情况下的核心流程。(会调用Bean的 destroy 方法,这个方法需要自己写自己配,方法名随意,配置destroy-method)
    注意:必须手动关闭Spring容器,容器才会销毁Bean

需要注意的三点:

  • 第一:只有正常关闭spring容器,bean的销毁方法才会被调用。
  • 第二:ClassPathXmlApplicationContext类才有close()方法。
  • 第三:配置文件中的init-method指定初始化方法。destroy-method指定销毁方法。
堆栈信息:
doCreateBean:557, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:522, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:337, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, AbstractBeanFactory$$Lambda$330/0x00000238591f5e98 (org.springframework.beans.factory.support)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:335, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:200, AbstractBeanFactory (org.springframework.beans.factory.support)
resolveCandidate:254, DependencyDescriptor (org.springframework.beans.factory.config)
doResolveDependency:1443, DefaultListableBeanFactory (org.springframework.beans.factory.support)
resolveDependency:1353, DefaultListableBeanFactory (org.springframework.beans.factory.support)
autowireResource:598, CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)
getResource:576, CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)
getResourceToInject:738, CommonAnnotationBeanPostProcessor$ResourceElement (org.springframework.context.annotation)
(beanName = userController)inject:270, InjectionMetadata$InjectedElement (org.springframework.beans.factory.annotation)
inject:145, InjectionMetadata (org.springframework.beans.factory.annotation)
postProcessProperties:368, CommonAnnotationBeanPostProcessor (org.springframework.context.annotation)
populateBean:1421, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:599, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:522, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:337, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, AbstractBeanFactory$$Lambda$330/0x00000238591f5e98 (org.springframework.beans.factory.support)
getSingleton:234, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:335, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:200, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:975, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:962, AbstractApplicationContext (org.springframework.context.support)
refresh:624, AbstractApplicationContext (org.springframework.context.support)
refresh:146, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:754, SpringApplication (org.springframework.boot)
refreshContext:456, SpringApplication (org.springframework.boot)
run:335, SpringApplication (org.springframework.boot)
run:1363, SpringApplication (org.springframework.boot)
run:1352, SpringApplication (org.springframework.boot)
main:10, DemoApplication (com.xun.demo)
代码验证

可以通过一个例子来确定这五步是正确的:
1、编写一个User类

/**
 * Bean的生命周期按照粗略的5步:
 *  第一步:实例化Bean。无参构造方法执行
 *  第二步:给Bean的属性赋值(调用Set方法)
 *  第三步:初始化Bean(会调用Bean的init方法,这个 init 方法需要自己写自己配)
 *  第四步:使用bean
 *  第五步:销毁Bean(会调用Bean的 destroy 方法,这个方法需要自己写自己配)
 */
public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
    private String name;

    public User() {
        System.out.println("第一步:User无参构造方法执行");
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步:给User对象的属性赋值");
    }
    
    // 方法名随意,只需要在xml中指定
    public void initBean() {
        System.out.println("第三步:初始化UserBean");
    }

    // 方法名随意
    public void destroyBean() {
        System.out.println("第五步:销毁UserBean");
    }
}

2、在resources文件夹中添加配置文件 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="user" class="com.xun.demo.dao.User" init-method="initBean" destroy-method="destroyBean">
        <property name="name" value="xun"/>
    </bean>
    
</beans>
  • <bean> 标签是Spring配置文件中定义一个Bean的基本元素。每一个Bean都是由这个标签来描述的。
  • id=“user” 是给这个Bean分配的一个唯一标识符。在Spring容器中,可以通过这个ID来引用这个Bean。
  • class=“com.xun.demo.dao.User” 指定了Bean的具体实现类,这里是com.xun.demo.dao.User类。这意味着Spring将创建这个类的实例,并管理它的生命周期。
  • init-method=“initBean” 定义了一个初始化方法,当Bean被Spring容器创建并完成依赖注入后,会调用这个方法。在这个例子中,initBean方法将在Bean初始化时执行。
  • destroy-method=“destroyBean” 定义了一个销毁方法,当Bean不再需要时,Spring容器会在销毁Bean之前调用这个方法。在这里,destroyBean方法将在Bean销毁前执行。
  • 这个子标签是用来进行依赖注入的。它指定了User类中的name属性应该被设置为字符串"xun"。这是所谓的属性注入(Property Injection)。
    这段配置告诉Spring框架创建一个com.xun.demo.dao.User类型的Bean,给它一个ID叫做user,在Bean创建后调用initBean方法,在Bean销毁前调用destroyBean方法,并且将name属性设置为"xun"。

3、然后在test中添加测试方法:

public class BeanLifeCycleTest {

    @Test
    public void testBeanLifeCycle() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User user = applicationContext.getBean("user", User.class);
        System.out.println("第四步:使用Bean:"+ user);

        // 注意:必须手动关闭Spring容器,容器才会销毁Bean
        ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
        context.close();
    }
}
执行结果为

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Bean生命周期之七步

在以上的5步中,第3步是初始化Bean,如果还想在初始化前和初始化后添加代码,可以加入“Bean后处理器”。
例如,加入一个日志打印器,再自行修改一下User中的输出内容:

public class LogBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第三步:执行Bean后处理器的 before 方法");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    /*
    方法有两个参数:
    第一个参数:刚创建的bean 对象
    第二个参数:bean的名字
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第五步:执行Bean后处理器的 after 方法");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

在spring.xml文件中配置“Bean后处理器”:

<!--配置Bean后处理器。这个后处理器将作用于当前配置文件中所有的bean。-->
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>

注意:在spring.xml文件中配置的Bean后处理器将作用于当前配置文件中所有的Bean。

执行结果

1、Bean后处理器的 before 方法:
在这里插入图片描述

2、Bean后处理器的 after 方法
在这里插入图片描述

3、七步结果
在这里插入图片描述

Bean生命周期之十步

在这里插入图片描述

增加的三步

检查Bean是否实现了Aware的相关接口:
Aware相关的接口(用于注入)包括:BeanNameAware、BeanClassLoaderAware、1、BeanFactoryAware

  • 当Bean实现了BeanNameAware,Spring会将Bean的名字传递给Bean。
  • 当Bean实现了BeanClassLoaderAware,Spring会将加载该Bean的类加载器传递给Bean。
  • 当Bean实现了BeanFactoryAware,Spring会将Bean工厂对象传递给Bean。

为了测试以上10步,需要让User类实现5个接口,并实现所有方法:
BeanNameAware:
BeanNameAware是Spring框架中的一个接口,用于使Bean能够意识到自己在BeanFactory中的名字。实现这个接口的Bean可以通过setBeanName方法获得其在Spring容器中的注册名称。

关键点解释:
接口目的:BeanNameAware接口的主要目的是允许Bean在初始化期间访问自己的名字。这在某些情况下可能是有用的,比如Bean需要根据其名字来执行某些特定的逻辑,或者在日志记录中引用自身时。

setBeanName方法:这是BeanNameAware接口中唯一需要实现的方法。它接收一个字符串参数name,代表Bean在BeanFactory中的注册名称。这个方法会在Bean的属性填充之后,但在初始化回调(如InitializingBean的afterPropertiesSet方法或自定义的初始化方法)之前被调用。

命名规则:需要注意的是,name参数可能与最初配置的bean名字有所不同。对于内部bean(inner bean),Spring可能会通过附加#…后缀来确保名字的唯一性。如果需要获取原始的bean名字(不带后缀),可以使用BeanFactoryUtils.originalBeanName(String)方法。

使用场景

  • 日志记录:在Bean的日志记录中包含其名字,以便更容易追踪和调试。
  • 动态配置:Bean可以根据其名字动态地调整行为,例如,根据名字的模式来决定是否启用某个功能。
  • 资源定位:Bean可能需要根据其名字来定位特定的资源,例如,读取与bean名字相关的配置文件或数据库表。

尽管BeanNameAware提供了访问bean名字的能力,但Spring文档中也提到这种依赖外部配置的方式可能带来脆弱性,因此建议谨慎使用。依赖于bean名字可能会限制应用的灵活性和可移植性,特别是在不同的部署环境中bean名字可能变化的情况下。因此,除非确实需要,否则应避免依赖于bean的名字。

2、BeanClassLoaderAware
BeanClassLoaderAware是Spring框架中的一个接口,用于使Bean能够意识到加载它的类加载器(ClassLoader)。实现这个接口的Bean可以通过setBeanClassLoader方法获得BeanFactory使用的类加载器,这个类加载器用于加载Bean的类。

关键点解释
接口目的:BeanClassLoaderAware的主要目的是为了支持那些需要根据类名动态加载类的框架类。在多类加载器环境下,框架类可能需要使用正确的类加载器来加载应用内的类,以避免类加载冲突或找不到类的问题。

setBeanClassLoader方法:这是BeanClassLoaderAware接口中唯一需要实现的方法。它接收一个ClassLoader参数,代表BeanFactory使用的类加载器。这个方法会在Bean的属性填充之后,但在初始化回调(如InitializingBean的afterPropertiesSet方法或自定义的初始化方法)之前被调用。

使用场景

  • 动态类加载:Bean可能需要动态地加载其他类,例如,根据配置信息加载不同的实现类。在这种情况下,使用正确的类加载器是非常重要的,特别是当应用运行在复杂的类加载器层次结构中时。
  • 资源访问:Bean可能需要访问由特定类加载器加载的资源,例如,从特定的类路径下读取配置文件或资源文件。
  • 兼容性处理:在某些情况下,Bean可能需要处理由不同类加载器加载的类之间的兼容性问题,例如,处理JAR包版本冲突。

注意事项
虽然BeanClassLoaderAware提供了一种访问类加载器的途径,但Spring文档中也强调了这种依赖可能带来的复杂性和潜在问题。在多类加载器环境下,类加载的顺序和可见性可能变得非常复杂,因此,除非确实需要,否则应尽量避免直接依赖类加载器。如果可能,应该优先考虑使用Spring的依赖注入和其他服务定位机制来避免直接的类加载器依赖。

3、BeanFactoryAware
BeanFactoryAware是Spring框架中的一个接口,用于使Bean能够意识到它们所属的BeanFactory。实现这个接口的Bean可以通过setBeanFactory方法获得对其所在BeanFactory的引用,从而允许Bean直接与BeanFactory交互,执行诸如查找其他Bean、访问配置元数据等操作。

关键点解释
接口目的:BeanFactoryAware的主要目的是为Bean提供一种方式,使其能够在初始化阶段获取到BeanFactory的引用。这使得Bean能够直接访问BeanFactory中的其他Bean或进行依赖查找(Dependency Lookup),而不仅仅是依赖注入(Dependency Injection)。

setBeanFactory方法:这是BeanFactoryAware接口中唯一需要实现的方法。它接收一个BeanFactory参数,代表Bean所属的BeanFactory实例。这个方法会在Bean的属性填充之后,但在初始化回调(如InitializingBean的afterPropertiesSet方法或自定义的初始化方法)之前被调用。

使用场景

  • 依赖查找:Bean可以使用BeanFactory查找并获取其他Bean的引用,这对于那些不能或不应该通过依赖注入来获取协作Bean的情况特别有用。
  • 配置元数据访问:Bean可以访问BeanFactory中关于其他Bean的元数据,例如,Bean的定义、属性值等。
  • 自定义初始化逻辑:Bean可以在初始化阶段执行一些基于BeanFactory的自定义逻辑,例如,根据环境配置动态地调整行为。

注意事项
尽管BeanFactoryAware提供了一种强大的机制,允许Bean直接访问BeanFactory,但这同时也可能引入耦合度较高的问题。通常,依赖注入(通过属性或构造函数)被认为是更优雅的设计模式,因为它减少了Bean之间的耦合,提高了代码的可测试性和可维护性。因此,除非有特殊需求,否则应优先考虑使用依赖注入而不是依赖查找。

4、InitializingBean
InitializingBean是Spring框架中的一个接口,设计用于让Bean在所有属性被BeanFactory设置完毕后执行自定义的初始化逻辑。实现这个接口的类必须提供一个afterPropertiesSet()方法,该方法会在Bean的所有属性被注入后调用,但在此之前会确保Bean已经满足了BeanFactoryAware、ApplicationContextAware等接口的要求(如果实现了的话)。

关键点解释
接口目的:InitializingBean接口使得Bean可以在其所有属性被设置完成后执行一些自定义的初始化动作,例如进行额外的配置检查或执行某些启动时的任务。

afterPropertiesSet()方法:这是InitializingBean接口中唯一需要实现的方法。它允许Bean进行最后的配置验证和初始化工作。如果在此过程中发现配置错误或初始化失败,该方法应当抛出异常,这将导致Spring容器无法继续初始化该Bean。

生命周期位置:afterPropertiesSet()方法在Bean的生命周期中处于一个关键的位置。它发生在依赖注入之后,但在Bean完全初始化(例如,调用init-method)之前。因此,这是一个很好的时机来检查Bean是否已正确配置,并进行必要的准备活动。

替代方案:除了实现InitializingBean接口,还可以通过在XML配置中指定一个init-method属性来达到类似的效果。但是,使用InitializingBean接口提供了一种更面向接口编程的方法,因为它不需要在每个Bean的配置中显式声明初始化方法。

使用场景

  • 配置验证:在afterPropertiesSet()中检查Bean的配置是否完整,例如,确保所有必需的属性都已经设置。
  • 资源初始化:在afterPropertiesSet()中初始化那些依赖于其他Bean属性的资源,例如数据库连接或文件系统资源。
  • 业务逻辑初始化:执行一些业务相关的初始化逻辑,例如加载数据或预热缓存。

总之,InitializingBean接口及其afterPropertiesSet()方法是Spring框架提供的一个强大工具,用于确保Bean在完全可用之前能够进行必要的自检和初始化工作。

5、DisposableBean
DisposableBean是Spring框架中的一个接口,用于使Bean能够在被销毁前释放其占用的资源。实现DisposableBean接口的Bean必须提供一个destroy方法,该方法将在Bean被销毁时由Spring容器调用。

关键点解释
接口目的:DisposableBean的主要目的是确保在Bean不再需要时,可以执行必要的清理操作,如关闭打开的文件、释放数据库连接或其他外部资源。这有助于避免资源泄漏和提高系统的健壮性。

destroy方法:这是DisposableBean接口中唯一需要实现的方法。它在Bean即将被销毁时由Spring容器调用。在该方法中,Bean可以执行任何必要的清理操作。如果在destroy方法中发生异常,这些异常会被记录,但不会阻止其他Bean的销毁过程。

使用场景

  • 资源释放:当Bean持有如文件句柄、网络连接、数据库连接等需要显式关闭的资源时,destroy方法可以用来确保这些资源在Bean生命周期结束时被适当地释放。
  • 清理工作:destroy方法可以用来执行任何在Bean生命周期结束时需要进行的清理工作,如清除临时文件、释放锁、取消定时任务等。

注意事项
调用时机:destroy方法的调用发生在Bean生命周期的末尾,即当Bean不再被需要并且将要从Spring容器中移除时。对于单例Bean,这通常发生在Spring容器关闭时;对于原型Bean,这发生在Bean实例被销毁时。
与Java的AutoCloseable接口:自Java 7起,Java引入了AutoCloseable接口,它也用于资源释放的目的。Spring支持Bean同时实现DisposableBean和AutoCloseable,或者仅实现AutoCloseable。如果Bean实现了AutoCloseable,Spring也会调用其close方法来释放资源。

替代方案:除了实现DisposableBean接口,还可以在XML配置文件中指定一个destroy-method属性,或者在基于注解的配置中使用@PreDestroy注解,来达到同样的效果。这些方法都允许在Bean销毁时执行自定义的清理逻辑。

通过测试可以看出来

  • InitializingBean的方法早于init-method的执行。
  • DisposableBean的方法早于destroy-method的执行。
    对于SpringBean的生命周期,掌握之前的7步即可。够用。
测试代码如下:
package com.xun.demo.dao;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;


/**
 *  Bean的生命周期按照粗略的5步:
 *  第一步:实例化Bean。无参构造方法执行
 *  第二步:给Bean的属性赋值(调用Set方法)
 *  第三步:初始化Bean(会调用Bean的init方法,这个 init 方法需要自己写自己配)
 *  第四步:使用bean
 *  第五步:销毁Bean(会调用Bean的 destroy 方法,这个方法需要自己写自己配)
 */
public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
    private String name;

    public User() {
        System.out.println("第一步:User无参构造方法执行");
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步:给User对象的属性赋值");
    }

    // 方法名随意
    public void initBean() {
        System.out.println("第六步:初始化UserBean");
    }

    // 方法名随意
    public void destroyBean() {
        System.out.println("第十步:销毁UserBean");
    }

    @Override
    public void setBeanName(String name) {
        //Spring会将Bean的名字传递给Bean
        System.out.println("第三步:BeanNameAware 接口,这个UserBean的名字是:"+ name);
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("第三步:BeanClassLoaderAware 接口,UserBean 这个类的加载器:" + classLoader);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("第三步:BeanFactoryAware 接口,生产这个UserBean的工厂对象是:" + beanFactory);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("第五步:InitializingBean 接口的 afterPropertiesSet 方法执行了");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("第九步:DisposableBean 接口的 destroy 方法执行了");
    }
}
执行结果:

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Bean的生命周期总结

在这里插入图片描述

ApplicationContext容器中,Bean的生命周期流程大致如下:

  1. 第一步:首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化
  2. 第二步:按照Bean定义信息配置信息,注入所有的属性。
  3. 第三步:Aware相关的接口
  4. 如果Bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id, 此时该Bean就获得了自己在配置文件中的id,
  5. 如果Bean实现了BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该Bean的BeanFactory,这样该Bean就获得了自己所在的BeanFactory,
  6. 如果Bean实现了ApplicationcontextAware接口,会回调该接口的setApplicationContext()方法,传入 该Bean的ApplicationContext,这样该Bean就获得了自己所在的ApplicationContext,
  7. 第四步:如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforelnitialzation()方 法
  8. 第五步:如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法
  9. 第六步:如果Bean配置了init-method方法,则会执行init-method配置的方法,
  10. 第七步:如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterlnitialization()方 法
  11. 第八步:可以正式使用该Bean了,对于scope为singleton的Bean,Spring的 ioc 容器中会缓存一份该bean的实例,而对于scope为prototype的Bean,每次被调用都会new一个新的对象,其生命周期就交给调用方管理了,不再是Spring容器进行管理了
  12. 第九步:容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法。
  13. 第十步:如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean的生命周期结束。

其他内容

如果对 Golang 实现的Raft共识算法、Golang实现的负载均衡或者 Golang web 项目的demo实现感兴趣的可以看看我的主页,各个部分都带有详细的代码解释,也可以通过代码仓库获取源码,直接运行。

Golang相关内容的gitee链接

标签:初始化,生命周期,Spring,bean,七步,Bean,源码,方法
From: https://blog.csdn.net/weixin_44615954/article/details/140578956

相关文章