《Spring从0到1》:配置创建、注入bean原理

手把手带你看spring的配置创建、注入以及获取bean。

看源码最好的方式就是定义一个最最基本的流程,从开始debug到你认为懂了为止。

bean的配置

最基本的spring配置是将bean配置在spring的配置文件中,该配置文件还可以配置spring的配置属性,虽然标签不是<bean>,但是spring会将其注入到指定的类中,因此不论标签如何,spring都会将配置文件中的属性转化成类属性,最终创建对应的对象。有的朋友可能已经注意到了:配置都是放在<beans>标签中的。

bean的生成与注入

启动spring的入口之一就是创建 ClassPathXmlApplicationContext 对象:

java 复制代码
//创建Spring容器的对象:ApplicationContext的实现类 ----> ClassPathXmlApplicationContext
//ApplicationContext就是表示Spring容器,我们可以通过容器获取对象
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");

向下看之前不妨看一下该类的UML关系:

创建ClassPathXmlApplicationContext对象时就能看到整个spring创建对象的流程:

真正调用的是这里: 这里的setConfigLocations 方法只是将传入的配置文件名注入到本类的configLocations字符串数组变量中。

真正的流程是在refresh 方法中,该方法的实现其实是在父类AbstractApplicationContext中:

将一些与主流程相关较小的代码剔除后如下:

scss 复制代码
public void refresh() throws BeansException, IllegalStateException {
   //在方法内对别的对象加锁,使得锁最小化。
   synchronized (this.startupShutdownMonitor) {
      //第一步就是创建bean的容器
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      prepareBeanFactory(beanFactory);

      try {
         
         postProcessBeanFactory(beanFactory);
         
         invokeBeanFactoryPostProcessors(beanFactory);

         registerBeanPostProcessors(beanFactory);

         finishBeanFactoryInitialization(beanFactory);
         
      } catch (BeansException ex) {...}

      finally {
         resetCommonCaches();
         contextRefresh.end();
      }
   }
}

逐步解析:

  1. obtainFreshBeanFactory():

    其中:refreshBeanFactory() 才是创建beanFactory的关键:其在 AbstractApplicationContext 类中是抽象方法,需要子类实现,通常调用的是 AbstractRefreshableApplicationContext 类。

    其中 loadBeanDefinitions(beanFactory) 是最主要的,在子类 AbstractXmlApplicationContext loadBeanDefinitions(beanFactory) 创建了 XmlBeanDefinitionReader 对象。该类封装了读配置文件的逻辑。随后就是调用 XmlBeanDefinitionReader 的方法 loadBeanDefinitions 这个方法在其父类 AbstractBeanDefinitionReader 中实现: AbstractBeanDefinitionReader 将字符串类型的配置文件名转化为 Resource 对象,Resource 是个接口,继承了InputStreamSource ,Spring中对于文件的读取类都需实现该接口。 而通过Resource加载类的方法是在子类 XmlBeanDefinitionReader 实现: 接着就是创建但是真正读配置文件的抽象类:BeanDefinitionDocumentReader registerBeanDefinitions 在子类 DefaultBeanDefinitionDocumentReader 中实现: 然后挨个解析每一个标签 这里的 BeanDefinitionHolder 要注意,这个就是解析bean标签的生成对象,可以看出,bean 标签里设置的属性都在该对象中,没有配置的属性会采用默认值(这也解释了spring中创建的对象默认是懒加载 的)。 随后将对象名和类定义放入【DefaultListableBeanFactory】beanDefinitionMap 中。

    而事实上,后续的流程都是针对 DefaultListableBeanFactory 类的。 就此,spring中一个完整的配置文件中bean的解析、注册就结束了!!!

  2. prepareBeanFactory(beanFactory)

    该方法就只是为Spring的BeanFactory赋值。比如后置处理器,添加一些Spring内部对象等:

    scss 复制代码
    /**
     * 配置BeanFactory的一些上下文属性,比如上下文类加载器和后置处理器
     * @param 需要赋值的beanFactory 
     */
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
       //使用调用类的类加载器
       beanFactory.setBeanClassLoader(getClassLoader());
       if (!shouldIgnoreSpel) {
          beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
       }
       beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
       // Configure the bean factory with context callbacks.
       beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
       beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
       beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
       beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
       beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
       beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
       beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
       beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);
    
       // BeanFactory interface not registered as resolvable type in a plain factory.
       // MessageSource registered (and found for autowiring) as a bean.
       beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
       beanFactory.registerResolvableDependency(ResourceLoader.class, this);
       beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
       beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
       // Register early post-processor for detecting inner beans as ApplicationListeners.
       beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    
       // Detect a LoadTimeWeaver and prepare for weaving, if found.
       if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
          beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
          // Set a temporary ClassLoader for type matching.
          beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
       }
    
       // 注册默认的环境类对象
       if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
          beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
       }
       if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
          beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
       }
       if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
          beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
       }
       if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
          beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
       }
    }
  3. invokeBeanFactoryPostProcessors(beanFactory):

    该方法主要是加载和实例化 BeanFactory 后置处理器(所有实现了 BeanFactoryPostProcessor 接口的类 )。后置处理器可以在 Bean 实例化、属性填充、初始化和销毁等不同的阶段对 Bean 进行自定义的处理和增强。典型的后处理器就是org.mybatis.spring.mapper.MapperScannerConfigurer,该类将指定的mapper接口注册到mybatisConfiguration类的MapperRegistry属性中。

  4. registerBeanPostProcessors(beanFactory):

    主要是将上一步实例化的后置处理器对象添加到 beanFactorybeanPostProcessors 变量中。

  5. finishBeanFactoryInitialization(beanFactory):

    从方法名也能看出该方法是最终的一步:初始化所有的单例模式,所有的非懒加载的类。该方法内封装的其实就是之前构建的 ConfigurableListableBeanFactory 接口实现类的方法,spring中该接口的实现类用的是 DefaultListableBeanFactory

可以看出,初始化类的时候是会根据类是否是工厂类而作出额外的初始化,最重要的还是 getBean 方法。 该方法是 ClassPathXmlApplication 的父类 AbstractApplicationContext 实现的: doGetBean 也是 AbstractBeanFactory 实现的:

scss 复制代码
protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {

   String beanName = transformedBeanName(name);
   Object beanInstance;

   // 看看有无该类对象的缓存,有则直接返回
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }else {
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      // 如果有父类的BeanFactory对象则调用父类BeanFactory独享的doGetBean方法。
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // Not found -> check parent.
         String nameToLookup = originalBeanName(name);
         if (parentBeanFactory instanceof AbstractBeanFactory) {
            return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                  nameToLookup, requiredType, args, typeCheckOnly);
         }
         else if (args != null) {
            // Delegation to parent with explicit args.
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else if (requiredType != null) {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
         else {
            return (T) parentBeanFactory.getBean(nameToLookup);
         }
      }

      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }

      StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
            .tag("beanName", name);
      try {
         if (requiredType != null) {
            beanCreation.tag("beanType", requiredType::toString);
         }
         RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // 先初始化要初始化对象依赖的对象,其实就是递归调用getBean。
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            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);
               }
            }
         }

         // 单例对象的初始化
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, () -> {
               try {
                  return createBean(beanName, mbd, args);
               }
               catch (BeansException ex) {
                  destroySingleton(beanName);
                  throw ex;
               }
            });
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
         //原型模式对象的创建
         else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }

         else {
            //Spring2.0之后又引入了另外三种scope类型:request、session、global session类型
            //只能在只能在Web应用中使用,若只是后台进程的话则不会走该分支。
            String scopeName = mbd.getScope();
            if (!StringUtils.hasLength(scopeName)) {
               throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
            }
            Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
               throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
            }
            try {
               Object scopedInstance = scope.get(beanName, () -> {
                  beforePrototypeCreation(beanName);
                  try {
                     return createBean(beanName, mbd, args);
                  }
                  finally {
                     afterPrototypeCreation(beanName);
                  }
               });
               beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            }
            catch (IllegalStateException ex) {
               throw new ScopeNotActiveException(beanName, scopeName, ex);
            }
         }
      }
      catch (BeansException ex) {
         beanCreation.tag("exception", ex.getClass().toString());
         beanCreation.tag("message", String.valueOf(ex.getMessage()));
         cleanupAfterBeanCreationFailure(beanName);
         throw ex;
      }
      finally {
         beanCreation.end();
      }
   }

   return adaptBeanInstance(name, beanInstance, requiredType);
}

doGetBean 方法是先看看该对象是否已经初始化,如果没有在创建并初始化,值得注意的是,其是先创建并初始化当前要创建的对象的依赖(还是调用doGetbean ),最后再根据对象类型分开创建,但是核心是调用 父类 AbstractAutowireCapableBeanFactorycreateBean 方法。 createBeanInstance 则很有意思,其是先判断是否由指定工厂创建,再有无注册的构造方法,最后才会通过反射获取该类的构造方法构造对象。 instantiate 方法在 SimpleInstantiationStrategy 类中。 BeanUtil.instantiateClass 方法则是封装的的通过构造器获取对象。 由此,一个对象就被创建结束,而后虽然会对该对象进行一系列封装,但是归根结底,spring的bean创建还是上述这些过程。

bean的获取

其实通过 ClassPathXmlApplicationContext 对象获取指定类的对象的 getBean 方法其实就是第5步中的方法,对于非懒加载类对象就可以直接获取,而懒加载类的对象则在此刻构建。

最后再附上一张用到的UML图:

相关推荐
Daniel 大东31 分钟前
BugJson因为json格式问题OOM怎么办
java·安全
Theodore_10225 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸6 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象6 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了7 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·7 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic7 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王7 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康7 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
qq_17448285758 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序