spring源码分析:bean的初始化

spring源码分析:bean的初始化

我们通过spring容器帮我们实例化并且维护bean的时候,有时候我们需要在bean在实例化完成的时候,帮我们做一些事情,这个时候我们就会使用到bean的初始化方法。举个例子,比如我们创建一个电脑,那么我们肯定就需要先安装系统,不然不能使用,此时我们就能把安装系统的过程封装到初始化方法中。我们今天主要来看我们常见的三种初始化的方法,并且分析一下他们的源码。

我们这里分析的三个初始化方法如下,如果已经比较了解如何使用以及原理,可以跳过该文章

1.实现InitializingBean接口

2.使用@PostConstruct

3.使用@Bean(initMethod = “xxx”)

这篇文章相对来说比较简单,但是还是希望大家对spring中的beanPostProcessor和bean创建的过程有了解。如果你不了解也没关系,相信这篇文章还是能给你带来收获

一.使用方式

使用方式就超级简单,下面简单的贴下代码

public class Computer implements InitializingBean {
   
    @Override
    public void afterPropertiesSet() throws Exception {
   
        System.out.println("调用afterPropertiesSet 完成初始化...安装系统");
    }


    @PostConstruct
    public void initByPostConstruct(){
   
        System.out.println("调用initByPostConstruct 完成初始化...安装系统");
    }


    public void init(){
   
        System.out.println("调用init 完成初始化...安装系统");
    }
}

另外,还有在config类里面通过@Bean实例化bean

@Configuration
@EnableAspectJAutoProxy
public class MainConfig {
   

    @Bean(initMethod = "init")
    public Computer computer(){
   
        return new Computer();
    }
}

上面同时使用了三种方式,正常情况下选择一种方式使用即可,我们看下执行打印的结果,顺便看下这三个是哪个先执行呢?

在这里插入图片描述

使用就到这里结束了,非常简单

二.源码分析

现在我们来分析他们三个实现的原理,第一可以更加深入的理解源码,第二万一面试被问到也不虚。这里就不再介绍bean创建的全过程了,如果感兴趣的可以参考我这篇文章(spring源码分析之如何解决循环依赖),这篇文章写的比较详细,我们直接从bean创建后,开始初始化的地方开始介绍

//AbstractAutowireCapableBeanFactory.java

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
   

   //....省略代码
   try {
   
       //属性赋值
      populateBean(beanName, mbd, instanceWrapper);
       //实现初始化的逻辑入口在这里
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   //省略代码
   
   return exposedObject;
}

上面直接从AbstractAutowireCapableBeanFactory.java开始分析,注释写的比较清楚,我们直接看initializeBean()

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
   
   if (System.getSecurityManager() != null) {
   
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
   
         invokeAwareMethods(beanName, bean);
         return null;
      }, getAccessControlContext());
   }
   else {
   
      invokeAwareMethods(beanName, bean);
   }

   Object wrappedBean = bean;
    //1.这里实现了@PostConstruct原理
   if (mbd == null || !mbd.isSynthetic()) {
   
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   try {
   
       //2.这里调用初始化方法,这里实现了InitializingBean和@Bean(initMethod="xx")的原理
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
   
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }
   if (mbd == null || !mbd.isSynthetic()) {
   
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }

   return wrappedBean;
}

我们先来看注释2的invokeInitMethods,看看他是如何实现初始化方法的

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
      throws Throwable {
   

    //1.实现InitializingBean原理
   boolean isInitializingBean = (bean instanceof InitializingBean);
   if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
   
      if (logger.isTraceEnabled()) {
   
         logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
      }
      if (System.getSecurityManager() != null) {
   
         try {
   
            AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
   
                //1-1
               ((InitializingBean) bean).afterPropertiesSet();
               return null;
            }, getAccessControlContext());
         }
         catch (PrivilegedActionException pae) {
   
            throw pae.getException();
         }
      }
      else {
   
          //1-2
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }
	//2.实现了@Bean(initMethod="xx")的原理
   if (mbd != null && bean.getClass() != NullBean.class) {
   
       //2-1这里会读取到initMethod方法,然后下面通过反射调用
      String initMethodName = mbd.getInitMethodName();
      if (StringUtils.hasLength(initMethodName) &&
            !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
   
          //具体的调用过程看这里,不做过多的解释
         invokeCustomInitMethod(beanName, bean, mbd);
      }
   }
}

注释1通过实现InitializingBean方式来实现初始化的非常的简单,不做过多的介绍,注释2通过@Bean的方式稍微介绍一下。

当我们通过@Bean方式还是之前通过xml配置bean时都能够指定initMethod属性,此时就会将这个bean先封装成BeanDefinition,同时配置的属性也会设置到BeanDefinition中,所以在这里我们是可以取出initMethod属性对应的方法,拿到之后然后实例耶创建出来了,自然就能通过反射调用。bean创建的过程都是先封装成beanDefinition,如果这一块不了解的可以先阅读相关文章

上面我们已经知道InitializingBean和@Bean(initMethod=“xx”)来实现初始化方法的原理,下面我们来看注释1通过@PostConstruct,直接对应下面方法

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {
   

   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
   
       //beanPostProcessor调用方法
      Object current = processor.postProcessBeforeInitialization(result, beanName);
      if (current == null) {
   
         return result;
      }
      result = current;
   }
   return result;
}

这其实就是beanPostProcessor,我们知道在spring中使用了大量的beanPostProcessor(我们自己也可以实现),通过它达到在spring创建过程中拦截的作用,在某个动作之前/之后做一些感兴趣的事情,达到“增强”的目的

这里其实就是用到了一个InitDestroyAnnotationBeanPostProcessor来实现初始化和销毁的过程,一定会执行postProcessBeforeInitialization

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   
    //找到要执行的方法
   LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
   try {
   
       //反射调用
      metadata.invokeInitMethods(bean, beanName);
   }
   catch (InvocationTargetException ex) {
   
      throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
   }
   catch (Throwable ex) {
   
      throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
   }
   return bean;
}

这里找到@PostConstruct的方法也很简单,无非就是获取方法的注释,并且判断是否是@PostConstruct,如果是则就算找到了,具体的细节感兴趣的可以自行阅读。

总的来说这篇文章还是比较简单,其实相对应的销毁方法也是一样的,感兴趣的可以自行阅读

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/111201.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • mediumtext_text长度不够用,改为mediumtext感觉 又太大,有没什么方法?

    mediumtext_text长度不够用,改为mediumtext感觉 又太大,有没什么方法?楼主先要搞清楚,text和longtext这些都是可变长度的字段类型.这是phpMyAdmin里的说明:text:最多存储65535(2^16-1)字节的文本字段,存储时在内容前使用2字节表示内容的字节数.longtext:最多存储4294967295字节即4GB(2^32-1)的文本字段,存储时在内容前使用4字节表示内容的字节数.也就是说,你在longtext类型的字段里只存1个字符,占用空…

    2022年5月1日
    79
  • WebStorm强大的调试JavaScript功能

    WebStorm强大的调试JavaScript功能一、JavaScript的调试目前火狐和Chrome都具备调试JavaScript的功能,而且还是相当的强大。如果纯粹是用浏览器来进行js调试的话,我比较喜欢用火狐。火狐可以安装各种插件,真的是非常适合开发者。不过今天的主角并不是火狐,也不是Chrome,而是号称最智能的JavaScriptIDE:WebStorm。WebStorm是jetbrains公司旗下一款JavaScript开发

    2022年10月23日
    0
  • 【软件工程】详细设计文档——详细设计说明书

    【软件工程】详细设计文档——详细设计说明书文章目录1引言1.1编写目的1.2项目背景1.3定义1.4参考资料2总体设计2.1需求概述2.2软件结构3模块描述3.1模块基本信息3.2功能概述3.3算法3.4模块处理逻辑3.5接口3.6性能3.7测试计划1引言1.1编写目的​ ​ 本报告的目的是对Asking进行详细设计说明,以便用户及项目开发人员了解产品详细的设计与实现。为开发人员提供开发参考书。以下叙述将结合文字描述、伪代码,图表等来描述Asking的详细设计和相关的模块描述。本报告的预期读者有客户、项目经

    2022年6月12日
    36
  • vsCode安装使用教程和插件安装

    vsCode安装使用教程和插件安装

    2021年10月2日
    73
  • 渗透测试工具——漏洞扫描工具

    渗透测试工具——漏洞扫描工具安全漏洞产生的原因技术原因软件系统复杂性提高,质量难于控制,安全性降低 公用模块的使用引发了安全问题经济原因“柠檬市场”效应——安全功能是最容易删减的部分环境原因从传统的封闭、静态和可控变为开放、动态和难控 攻易守难安全缺陷安全性缺陷是信息系统或产品自身“与生俱来”的特征,是其“固有成分”安全漏洞是与生俱来的系统设计缺陷Internet从设计时就缺乏安全的总体架构和设计 TCP/IP中的三阶段握手.软件源代码的急剧膨胀Windows951500万行

    2022年9月13日
    0
  • TS文件解码TS文件解密TS流批量下载和解码工具

    TS文件解码TS文件解密TS流批量下载和解码工具TS的全称则是TransportStream,即传输流,DVD节目中的MPEG2格式,是MPEG2-PS,MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。现主流视频网站都采用这种模式。m3u8是一个TS切片列表文件,它记录视频的每个切片的时长与顺序,下面通过图片了解一下:怎么得到视频网站中的m3u8文件呢?…

    2022年7月18日
    21

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号