前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring学习(一)bean的初始化过程

Spring学习(一)bean的初始化过程

作者头像
虞大大
发布2020-08-26 17:20:12
2.4K0
发布2020-08-26 17:20:12
举报
文章被收录于专栏:码云大作战码云大作战

一、Spring注入示例

这里以注解的形式来进行注入示例的演示。

(1)通过@ComponentScan扫描com.ywl.leetcode下面所有的类。

代码语言:javascript
复制
@ComponentScan("com.ywl.leetcode")
public class AppConfig {
}

(2)申明一个类并加上@Component注解,无参构造函数中打印日志记录。

代码语言:javascript
复制
@Component
public class TestService {
    public TestService() {
        System.out.println("testService创建结束");
    }
}

(3)启动spring容器,观察运行结果。

代码语言:javascript
复制
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    TestService testService = context.getBean(TestService.class);
}

(4)运行结果

运行完毕,TestService这个bean创建完毕。

二、疑问点:TestService这个bean是在spring容器启动时被创建出来的还是在context.getBean时候被创建出来的?

验证方式:注释getBean代码。仍打印创建结束日志,说明是在spring容易启动时被创建出来的。

代码如下:

代码语言:javascript
复制
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//        TestService testService = context.getBean(TestService.class);
    }
//运行结果testService创建结束

三、阅读spring源码来验证bean初始化的过程

(1)普通类的java初始化过程。

java文件经过虚拟机编译,编译成.class字节码文件,当运行main方法时,会启动JVM虚拟机,通过虚拟机从磁盘上将.class字节码文件存储到方法区或者元数据空间,当对象被new出来时,会在堆上分配一块内存用来存储这个对象。

(2)bean的初始化过程。

开始的初始化过程与普通类的初始化过程一样,会由jvm分配一块内存空间给这个对象。当spring容器开始加载时,首先会解析AppConfig.class。

发现AppConfig中指定了扫描路径,需要找到扫描路径中需要被spring容器加载的类,即加了@Component、@Service、@Controller等注解的类,对这些类进行解析。

解析后,这些类会被构建成一个spring中的BeanDefinition类,来存储类中的所有基本信息,比如beanClassName(类的type)、parentName(类的父类名字)、scope(类的作用域)、lazyInt(是否懒加载)等。

BeanDefinition详细存储信息可以查看org.springframework.beans.factory.config.BeanDefinition。

构建成BeanDefinition后会把他放到一个map中,key为beanName,value为BeanDefinition。

最后对这些类进行遍历,会在spring加载时对单例类并且不是懒加载的类进行bean的初始化,初始化完毕后,会放入到一个单例池的map中,即singletonMap。

而对于那些不是单例或者是懒加载的类,就会在调用getBean方法时被初始化出来。

· 查看TestService的beanDefinition

代码语言:javascript
复制
public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    BeanDefinition beanDefinition = context.getBeanDefinition("testService");
    System.out.println(beanDefinition);
}

beanDefinition里面存储的信息:

代码语言:javascript
复制
// Generic bean: class [com.ywl.leetcode.spring.ready.TestService];
// scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0;
// autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null;
// initMethodName=null; destroyMethodName=null;

· java对象和spring bean的区别

上述初始化过程中可以看到java对象和bean其实有很大的区别。总而言之spring bean是一个java对象,而java对象不一定是一个bean。

因为我读完spring源码后,个人认为bean与java对象最大的区别在于,java对象就是一个普通的对象,而bean是一个具有spring生命周期的一个对象。

· 调用AnnotationConfigApplicationContext

代码语言:javascript
复制
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
   this();
   register(annotatedClasses);
   refresh();
}

开启断点进行调试,发现refresh方法执行完毕,发现构造函数中的日志被打印,说明初始化的核心方法在refresh中。

· refresh()

代码语言:javascript
复制
synchronized (this.startupShutdownMonitor) {      //...      //这里主要是扫描指定路径下加了主键的类,并放入到beanDefinitionMap中
      invokeBeanFactoryPostProcessors(beanFactory);
      //...

在执行invokeBeanFactoryPostProcessors之前的beanDefinitionMap没有我们需要的类。

执行完后,出现了testService。

debug执行完finishBeanFactoryInitialization后发现,打印了构造函数中的日志。说明bean初始化的核心方法在该方法中。

代码语言:javascript
复制
   // ...
代码语言:javascript
复制
   finishBeanFactoryInitialization(beanFactory);
   // ...

· finishBeanFactoryInitialization()

核心代码在beanFactory.preInstantiateSingletons中,该方法的作用为加载非懒加载的单例bean。

//...

beanFactory.preInstantiateSingletons();

代码语言:javascript
复制
代码语言:javascript
复制
//...

· preInstantiateSingletons()

代码语言:javascript
复制
@Override
public void preInstantiateSingletons() throws BeansException {
   //...   //获取需要加载的所有beanName,进行遍历   List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
   for (String beanName : beanNames) {      //根据beanDefinitionMap获取beanDefinition      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      //不是抽象类 并且 是单例 并且 不是懒加载 才可以在这时候被初始化      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {         //判断是否是factoryBean         if (isFactoryBean(beanName)) {
            final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
               isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                  @Override
                  public Boolean run() {
                     return ((SmartFactoryBean<?>) factory).isEagerInit();
                  }
               }, getAccessControlContext());
            }
            else {
               isEagerInit = (factory instanceof SmartFactoryBean &&
                     ((SmartFactoryBean<?>) factory).isEagerInit());
            }
            if (isEagerInit) {
               getBean(beanName);
            }
         }         else {            //testService会走进这里来初始化bean            getBean(beanName);
         }
      }
   }
   //...
}

上述的方法其实很简单,遍历所有需要初始化的bean,就是遍历存储beanName的list,并根据beanName作为key去查询beanDefinitionMap中的beanDefinition,校验对应的类,只有不是抽象类、是单例、不是懒加载的类才可以在spring容器初始化时被初始化。

bean初始化的方法在getBean(beanName)中。

四、核心方法doGetBean()

代码语言:javascript
复制
protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {   //校验beanName合法性
   final String beanName = transformedBeanName(name);
   Object bean;
   //查询缓存池中是否存在该bean   //当前处于spring加载时,所以testService肯定为null
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {         //校验是否bean在初始化中         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   else {
      //bean是否在初始化中,循环依赖会涉及到
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }
      //...
      try {
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);
         // 查询该bean是否存在depend on依赖的其他bean
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {            // 遍历依赖的bean,对bean进行查找与初始化            for (String dep : dependsOn) {
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               registerDependentBean(dep, beanName);
               try {
                  getBean(dep);
               }
               catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
               }
            }
         }

         //bean是否为单例,testService为单例,所以会走进这个分支
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
               @Override
               public Object getObject() throws BeansException {
                  try {                     //创建bean
                     return createBean(beanName, mbd, args);
                  }
                  catch (BeansException ex) {
                     destroySingleton(beanName);
                     throw ex;
                  }
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
         //bean作用域为原型的会进这个分支初始化bean
         else if (mbd.isPrototype()) {
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }             //...}

doGetBean()中存放许多核心的方法,比如getSingleton、createBean等,doGetBean大致的流程为上述源码展示的那样:

(1)会去缓存池中查找该bean是否被加载过,如果被加载过,返回。由于此时spring加载时,会加载testService,因此testService该bean不存在缓存池中。

(2)进行单例作用域与原型作用域bean的创建,由于testService为单例因此,我们只需要关注单例的创建即可。

除此之外我们需要关注几个核心的方法:

· getSingleton

代码语言:javascript
复制
protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

大致的意思为:

(1)先从singletonObjects一级缓存中查询是否有bean,如果有则返回。

(2)判断是否bean处于正在初始化中,这个条件主要是为了循环依赖使用,循环依赖情况下,可能存在bean正在创建中的情况。这种情况今天的初始化过程先不涉及。

(3)去三级缓存earlySingletonObjects中查询是否有bean,如果有则返回。

(4)去二级缓存singletonFactories中查询是否存在bean的工厂,如果存在,获取该bean工厂对应的bean,放到三级缓存中。返回。

上述代码中展示了spring容器总共有三级缓存,来保证获取bean的性能。

· createBean

代码语言:javascript
复制
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
   //...
代码语言:javascript
复制
   // 从beanDefinition中取出该bean的class类型
代码语言:javascript
复制
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }
   //...
   //debug到此处运行完毕后发现,运行了构造函数的日志打印
   Object beanInstance = doCreateBean(beanName, mbdToUse, args);
   if (logger.isDebugEnabled()) {
      logger.debug("Finished creating instance of bean '" + beanName + "'");
   }
   return beanInstance;
}

· doCreateBean

代码语言:javascript
复制
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
      throws BeanCreationException {
   //...
if (instanceWrapper == null) {//实例化对象instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   //...
}

调用完createBeanInstance后对象被new出来,并执行了构造函数中的日志打印。createBeanInstance中做的事情就是推断出合适的构造函数,通过反射来构造对象。

此时的instanceWrapper只是一个普通的对象,但是并不是一个bean。

修改代码如下图所示:

代码语言:javascript
复制
@Component
public class TestService {

    @Autowired
    private UserService userService;
    public TestService() {
        System.out.println("testService创建结束");
    }
}

通过@Autowired自动注入userSerivce,debug到createBeanInstance执行完毕后发现此时的userSerivce并没有被注入。只是对象被构造完成,执行完了构造函数。

此时的testSerivce还不是bean,也可以使用指定testService的初始化方法来观察,有没有执行bean的初始化方法。

运行到createBeanInstance后,没有执行bean的初始化方法。

继续执行代码:

代码语言:javascript
复制
//判断是否允许循环依赖//条件一:单例//条件二:私有属性默认为true,不过可以修改,私有属性在beanFactory中//条件三:是否处于正在创建队列中。在调用createBean方法之前,已经放在了创建队列中。boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
   if (logger.isDebugEnabled()) {
      logger.debug("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
   }   //将bean生成的bean工厂放到二级缓存中   addSingletonFactory(beanName, new ObjectFactory<Object>() {
      @Override
      public Object getObject() throws BeansException {         //这里主要判断是否存在aop,在后面的spring aop中会讲到,这里不做阐述
         return getEarlyBeanReference(beanName, mbd, bean);
      }
   });
}
//...

继续执行代码,发现在populateBean之后完成了userService的注入:

代码语言:javascript
复制
//...bject exposedObject = bean;
try {
   populateBean(beanName, mbd, instanceWrapper);
   if (exposedObject != null) {
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
}//...

· populateBean

populateBean主要会解析注入时使用的注解比如@Autowired或@Resource或者Xml文件注入来进行依赖注入。最后都会调用beanFactory.getBean对需要依赖注入的bean进行初始化。

· 回到doGetBean继续执行getSingleton方法

源码如下:

代码语言:javascript
复制
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "'beanName' must not be null");
   synchronized (this.singletonObjects) {       //先从一级缓存中获取,是否存在bean      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         //...         //这里会将该bean放到集合中,表示正在创建该bean         beforeSingletonCreation(beanName)         //...         try {//该singletonFactory表示,调用createBean创建出来的beanFactory            //根据bean工厂获取工厂中的bean
            singletonObject = singletonFactory.getObject()            newSingleton = true;
         }
         //...
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }            //从正在创建bean的集合中将该bean移除,意味着bean马上就要创建成功了            afterSingletonCreation(beanName);
         }
         if (newSingleton) {            //将bean放入到一级缓存中            addSingleton(beanName, singletonObject);
         }
      }
      return (singletonObject != NULL_OBJECT ? singletonObject : null);
   }
}

五、普通bean的初始化过程-总结

以上就是普通spring bean的初始化过程,跟着debug阅读bean的初始化过程其实并不复杂,期间有比较多的参数以及为什么需要用到三级缓存,在接下来的循环依赖和aop中都会涉及到,先可以放下这些细节,学习下在没有循环依赖情况下bean的初始化过程。

用画图来总结bean的过程其实是这样的:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码云大作战 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档