Spring Bean 的生命周期

简介: Bean 的初始化学习,我们先梳理一下 Spring Bean 初始化过程的步骤

Bean 的初始化学习,我们先梳理一下 Spring Bean 初始化过程的步骤


1. 源码入口 AnnotationConfigApplicationContext#refresh()


// 执行 BeanFactoryPostProcessors
invokeBeanFactoryPostProcessors(beanFactory);
// 进入实现(快捷键:ctrl + alt + b )
// AbstractApplicationContext#invokeBeanFactoryPostProcessors
// 1. 执行 BeanFactoryPostProcessors 的方法
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());


Spring Bean 的创建过程


1. BeanDefinition 获取 Bean 定义信息


  • 定义配置类和扫描的路径/扫描的包


// 定义配置类和扫描的路径/扫描的包
@Configuration
@ComponentScan("cn.edu.cqvie.service")
class AppConfig {
  @Bean
  public UserService userService200() {
    return new UserService();
  }
}


  • 生成 BeanDefinition 对象入口, 按照这个入口结构可以方便我们调试


//生成 BeanDefinition 对象入口
AnnotationConfigApplicationContext#refresh()
-- AbstractApplicationContext#invokeBeanFactoryPostProcessors(..);
  -- PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(..);
    -- PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors(..)
       -- ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry(..)
       --   processConfigBeanDefinitions(..)
         -- ConfigurationClassParser#parse(..)
         --   processConfigurationClass(..)
         --   doProcessConfigurationClass(..)
           -- ComponentScanAnnotationParser#parse(..)
             -- ClassPathBeanDefinitionScanner#doScan(..)
             -- findCandidateComponents(..)
             -- scanCandidateComponents(..)
             // 扫描包: Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);` 
             // 最终返回  Set<BeanDefinition> 生成 BeanDefinition 


  • 扫描方法 scanCandidateComponents 解析


 1). 扫描包获取到 Resource 数组


 2). 遍历 Resource 数组然后生成通过 ASM 模块获取 MetadataReader 对象获取 class 上的元信息和注解信息  


 3). 然后通过 isCandidateComponent(..) 方法判断该 class 文件是否包含@Component 信息,或者说是否是一个 Bean


 4). 如果通过检查则将它加入到 candidates 中去并且返回。


// 扫描方法
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    //扫描类,得到 BeanDefinition
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            if (resource.isReadable()) {
                try {
                    //MetadataReader 包含了对应的 class 的元信息以及注解信息, MetadataReader
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    //判断是否是 Component
                    if (isCandidateComponent(metadataReader)) {
                        //通过扫描 @Component 得到 BeanDefinition为 ScannedGenericBeanDefinition
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        //再次验证是否是 Component
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            candidates.add(sbd);
                        }
                        else {
                            if (debugEnabled) {
                                logger.debug("Ignored because not a concrete top-level class: " + resource);
                            }
                        }
                    }
                    else {
                        if (traceEnabled) {
                            logger.trace("Ignored because not matching any filter: " + resource);
                        }
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to read candidate component class: " + resource, ex);
                }
            }
            else {
                if (traceEnabled) {
                    logger.trace("Ignored because not readable: " + resource);
                }
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
}


2. 合并 BeanDefinition


如果某个 BeanDefinition 存在父 BeanDefinition, 那么则要进行合并


RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);


3. 加载类


1. 如果当前类不是懒加载,且是单例 Bean 我们就去加载类


// 方法入口
AbstractAutowireCapableBeanFactory#createBean(..) {
  // 加载类
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
}

2. 类加载方法 resolveBeanClass 的实现


if (mbd.hasBeanClass()) {
  return mbd.getBeanClass();
}
if (System.getSecurityManager() != null) {
  return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ->
    doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
}
else {
  return doResolveBeanClass(mbd, typesToMatch);
}


4. 实例化前置方法


Spring 允许第三方自定义 Bean 创建过程的拓展方式,可以利用InstantiationAwareBeanPostProcessorspostProcessBeforeInstantiation 来实现 Bean 的创建,已经结束默认的创建过程。


5. 推断构造方法


后续拓展单独来写


6. 实例化


通过构造方法反射获取到一个 Bean 的实例


// 方法入口
SimpleInstantiationStrategy#instantiate()
// 获取实例
return BeanUtils.instantiateClass(constructorToUse);


7. BeanDefinition 的后置处理


BeanDefintion 后置处理方法


// 方法入口
AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors(...)


执行 BeanDefinition 逻辑的代码


for (BeanPostProcessor bp : getBeanPostProcessors()) {
  if (bp instanceof MergedBeanDefinitionPostProcessor) {
    MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
    bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
  }
}


8. 属性填充


后续拓展单独来写


9. 执行Aware


我们常用到的 ApplicationContextAware 有主要有BeanNameAwareEmbeddedValueResolverAwareApplicationContextAwareMessageSourceAware我个人用到的比较多的就是 ApplicationContextAware 举个例子:


  1. 通常我们项目中有一个工具类 ApplicationContextUtil,通过 Spring Aware 拓展,可以提供拿到 bean 容器 ApplicationContext,最后可以获取到到目标 Bean。


  1. 我们可以使用下面的方式,代码实例


@Component
public class ApplicationContextUtil implements ApplicationContextAware {
    private static ApplicationContext context;
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }
    public static ApplicationContext getApplicationContext() {
        return context;
    }
}
//调用方法,如果已经注册UserServie
UserService userSerivce = ApplicationContextUtil.getApplicationContext().getBean(UserServie.class);


Aware 的逻辑如下:


private void invokeAwareMethods(final String beanName, final Object bean) {
  if (bean instanceof Aware) {
    if (bean instanceof BeanNameAware) {
      ((BeanNameAware) bean).setBeanName(beanName);
    }
    if (bean instanceof BeanClassLoaderAware) {
      ClassLoader bcl = getBeanClassLoader();
      if (bcl != null) {
        ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
      }
    }
    if (bean instanceof BeanFactoryAware) {
      ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
    }
  }
}


10. 初始化前


这里需要注意的是在 BeanPostProcessorpostProcessBeforeInitialization 的方法中如果返回 null 那么后续的 Bean 后置处理器就不会被执行。


public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // bpp1 => bpp2 ==> bpp3 (这里相当于是一个过滤器)
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}


11. 初始化


初始化过程中主要是做了一个判断,如果当前 Bean 实现了 InitializingBean 接口那么将调用 Bean 的初始化方法afterPropertiesSet()


((InitializingBean) bean).afterPropertiesSet();


12. 初始化后


Bean 初始化后,执行 Bean 后置处理器 postProcessAfterInitialization 方法


for (BeanPostProcessor processor : getBeanPostProcessors()) {
  Object current = processor.postProcessAfterInitialization(result, beanName);
  if (current == null) {
    return result;
  }
  result = current;
}


Bean 的销毁过程


上面我们讲到了 Spring Bean 的初始化,那么我们现在来看看 Spring Bean 销毁过程。这里由于 Bean 有两种模式:单例模式和原型模式,原型模式没有被 IOC 管理在实用完过后,由 GC 回收。单例模式被 IOC 容器缓存那么被创建单例实例 Bean 的生命周期就是在被创建后知道 IOC 容器关闭的这个过程,所以 Bean 销毁的核心就是 Bean 容器的关闭过程。


我们可以通过下面的代码来关闭容器。


//关闭 ioc 容器
applicationContext.close();


1. 容器关闭


2. 发布 ContextCloseEvnet 事件


3. 调用 LifecycleProcessor 的 onClose 方法


4. 销毁单例 Bean


  1. 找出所有的 Disposable Bean (实现了 DisposableBean 接口的 Bean)


  1. 遍历每个 DisposableBean Bean


  1. 找出它所依赖的 Bean ,并且将这些 Bean 在单例池中移除掉。


  1. 调用 DisposableBean 的 destroy() 方法


  1. 找到当前 DisposableBean 所包含的 inner beans ,将这些 Bean 从单例池中移除掉。


初始化和销毁拓展方法


@PostConstruct@PreDestroy 注解主要是用来实现在 Bean 初始化过后,和 Bean 被销毁的时候执行的拓展方法。比如:比如我需要把某个 Bean 初始化后赋值给一个 static field 就可以通过 @PostConstruct 方式来完成,比如关闭 socket session 就可以在 @PreDestroy 来完成。代码示例:


/**
 * 初始化方法
 */
@PostConstruct
public void init() {
  System.out.println("int invoke!");
}
/**
 * 销毁方法方法
 */
@PreDestroy
public void destroy() {
  System.out.println("destroy invoke!");
}


Spring 源码解析










相关文章
|
21天前
|
XML 安全 Java
|
3天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
21 6
|
5天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
33 3
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
18天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
28 1
|
2月前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
80 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
3月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
318 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
3月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
250 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
3月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean