头图

bean loading

The previous article mainly analyzed the parsing of the XML configuration file, and the next step was to analyze the loading of the bean, and also began to use the initial code as the entry.

Entry code getBean

 public void testSimpleLoad(){
   final BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
   final MyTestBean myTestBean = (MyTestBean) beanFactory.getBean("myTestBean");
   assertEquals("testStr",myTestBean.getTestStr());
}

From here, let's take a quick first look at how it is implemented.

From the BeanFactory interface, we choose the corresponding implementation class as AbstractBeanFactory.

 Object getBean(String name) throws BeansException;
 @Override
public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}
 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        //通过三种形式获取beanName, 一个是原始的beanName,一个是加了&的,一个是别名
        //返回容器中真是的beanName
        final String beanName = transformedBeanName(name);
        Object bean;

        /**
         * 检查缓存中或实例工厂中是否存在对应实例
         * 因为在创建单例bean的时候会存在依赖注入的情况,Spring为了避免循环依赖,创建bean的原则是不等bean创建完成就会创建bean的ObjectFactory提早曝光
         * 也就是将ObjectFactory放入到了缓存中,一旦下个bean创建时候需要依赖上一个bean则直接使用ObjectFactory
         */
        // Eagerly check singleton cache for manually registered singletons.
        //尝试从缓存中获取或者从singleFactories中的ObjectFactory中获取
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            if (logger.isTraceEnabled()) {
                //如果Bean还在创建中,则说明是循环引用
                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 + "'");
                }
            }
            //如果是普通bean,直接返回,如果是FactoryBean,则返回它的getObject
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            //只有在单例情况下Spring才会尝试解决循环依赖,在原型模式下如果存在A->B->A的话就会抛出异常
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
            //检查工厂中是否存在bean定义
            BeanFactory parentBeanFactory = getParentBeanFactory();
            //如果beanDefinitionMap中不包括当前beanName则会尝试从parentBeanFactory中检测
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //主要针对FactoryBean,将Bean的&重新加上
                //将转换过后的BeanName恢复回原先的样子
                String nameToLookup = originalBeanName(name);
                if (parentBeanFactory instanceof AbstractBeanFactory) {
                    //递归到BeanFactory中寻找
                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
                            nameToLookup, requiredType, args, typeCheckOnly);
                }
                else if (args != null) {
                    //如果有参数,则委派父类容器根据指定名称和参数查找
                    return (T) parentBeanFactory.getBean(nameToLookup, args);
                }
                else if (requiredType != null) {
                    //委派父级容器根据指定名称和类型查找
                    return parentBeanFactory.getBean(nameToLookup, requiredType);
                }
                else {
                    //委派父级容器根据指定名称查找
                    return (T) parentBeanFactory.getBean(nameToLookup);
                }
            }
            //如果不是仅仅做类型检查则是创建bean,进行记录
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                //将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,如果指定的BeanName是子bean的话还会合并父类的相同属性
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                //对合并的BeanDefinition做验证,主要看属性是否为abstract的
                checkMergedBeanDefinition(mbd, beanName, args);

                String[] dependsOn = mbd.getDependsOn();
                //如果存在依赖则需要递归实例化依赖的bean
                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方法,注册Bean之间的依赖(如C需要晚于B初始化,而B需要晚于A初始化)
                            //初始化依赖的bean
                            getBean(dep);
                        }
                        catch (NoSuchBeanDefinitionException ex) {
                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                        }
                    }
                }
                // 实例化依赖的bean后就可以实例化mbd本身了
                // 如果BeanDefinition为单例
                if (mbd.isSingleton()) {
                    //创建Bean实例对象,并且注册给所依赖的对象
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        catch (BeansException ex) {
                            //从单例缓存中删除bean实例
                            //因为单例模式下为了解决循环依赖,可能它已经存在了,所以将其销毁
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    //如果是普通bean,直接返回,如果是FactoryBean,则返回它的getObject
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }
                // 如果BeanDefinition为prototype
                else if (mbd.isPrototype()) {
                    // 每次创建一个新的对象
                    Object prototypeInstance = null;
                    try {
                        //注册当前创建的prototype对象为正在创建中
                        beforePrototypeCreation(beanName);
                        //创建原型对象实例
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        //将先前注册的正在创建中的Bean信息给抹除掉
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
                //如果不是原型模式和单例模式则可能是: request、session、application等生命周期
                else {
                    String scopeName = mbd.getScope();
                    final Scope scope = this.scopes.get(scopeName);
                    //Bean定义资源中没有配置生命周期范围,则Bean定义不合法
                    if (scope == null) {
                        throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                    }
                    try {
                        //如果bean的scope不是singleton和prototype,则调用scope.get()来选择合适的加载策略
                        Object scopedInstance = scope.get(beanName, () -> {
                            //注册当前创建的prototype对象为正在创建中
                            beforePrototypeCreation(beanName);
                            try {
                                //创建bean
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                //将先前注册的正在创建中的Bean信息给抹除掉
                                afterPrototypeCreation(beanName);
                            }
                        });
                        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                    }
                    catch (IllegalStateException ex) {
                        throw new BeanCreationException(beanName,
                                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                                ex);
                    }
                }
            }
            catch (BeansException ex) {
                cleanupAfterBeanCreationFailure(beanName);
                throw ex;
            }
        }

        // Check if required type matches the type of the actual bean instance.
        //检查需要的类型是否符合bean的实际类型
        if (requiredType != null && !requiredType.isInstance(bean)) {
            try {
                T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
                if (convertedBean == null) {
                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
                }
                return convertedBean;
            }
            catch (TypeMismatchException ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Failed to convert bean '" + name + "' to required type '" +
                            ClassUtils.getQualifiedName(requiredType) + "'", ex);
                }
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            }
        }
        //返回bean实例
        return (T) bean;
}

You can see that there is a lot of code in this method. Let's sort out the process first:

1. Convert the corresponding beanName

Converting the corresponding beanName means that the beanName we pass in here may be an alias or a FactoryBean, so it needs to be parsed.

include:

  • Remove the FactoryBean modifier, that is, if name=&jack, then the & will be removed and name=jack
  • Take the final beanName represented by the specified alias, if the alias A points to the bean of the name B, then return the bean of B; if the alias A points to the alias B, and the alias B points to the alias C, then the bean of C is returned

2. Attempt to load the singleton from the cache

A singleton is only created once in the same container of Spring, and subsequent acquisitions do not need to be created again, but are obtained directly from the singleton cache. But here is also trying to load the bean, first from the cache, and if the load is unsuccessful then from the singletonFactories . 因为在创建单例bean的时候可能会存在依赖注入的情况,Spring在创建依赖的时候为了避免循环依赖,创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提前曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean则直接使用ObjectFactory .

3. bean instantiation

If the uninstantiated bean is obtained from the cache, it needs to be instantiated. It should be noted that the most original bean state is recorded in the cache, not necessarily the bean we want. What we need is the bean returned in the factory-method method defined in the factory bean, and the getObjectForBeanInstance method does the job.

4. Prototype pattern dependency checking

Only in the case of a singleton will try to solve the circular dependency. If there is a B property in A, and A property in B, when dependency injection occurs, it will be generated when A has not been created because the creation of B will return to create A again , resulting in a circular dependency. That is isPrototypeCurrentlyInCreation(beanName) is true.

5. Detect parentBeanFactory

 BeanFactory parentBeanFactory = getParentBeanFactory();

If it is not obtained from the cache, it will be loaded from the parent class factory.

The next line to judge, if parentBeanFactory is empty everything is a cloud, and then to detect if the currently loaded XML configuration does not contain the configuration corresponding to beanName, you can only try in parentBeanFactory, and then recursively call the getBean method.

 if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {

6. Convert the GenericBeanDefinition that stores the XML configuration file to a RootBeanDefinition

As mentioned earlier, the bean information read from XML is stored in GenericBeanDefinition, but the subsequent bean processing is for RootBeanDefinition, so a conversion is required, and if the parent class bean is judged to be not empty, the parent class is merged together. Attributes.

 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

7. Find dependencies

Some properties may be used in bean initialization, and some properties may be dynamically configured and depend on other beans. In this case, the dependent beans need to be loaded first.

8. Create beans for different scopes

This stage creates beans for different scopes, the default is singleton, as well as prototype, request and so on.

9. Type conversion

At this point, the creation of the bean is basically over. In this step, a conversion is performed on the bean, and if requiredType != null is judged, it will be converted to the actual type. After these steps, the creation of the bean is complete, and then the bean we need is returned.

Summarize

  1. Check whether there is a loaded bean in the cache through the getSingleton(beanName) method
  2. If the condition is true if (sharedInstance != null && args == null) , call the getObjectForBeanInstance(sharedInstance, name, beanName, null) method to get the bean instance, but sometimes there is a situation such as BeanFactory that does not directly return the instance itself but returns the instance of the specified method, and jumps to Step 6
  3. if (parentBeanFactory != null && !containsBeanDefinition(beanName)) If there is no beanName currently, call the method parentBeanFactory.getBean to find it in the parent class factory, the parent class factory is usually empty
  4. getMergedLocalBeanDefinition(beanName) Convert the GenericBeanDefinition that stores the XML configuration file into a RootBeanDefinition. If the specified beanName is a child bean, it will also merge the parent class related properties Text
  5. if (mbd.isSingleton()) else if (mbd.isPrototype()) differently according to the attribute scope of the attribute bean
  6. if (requiredType != null && !requiredType.isInstance(bean)) The type conversion is performed by specifying that the requirement type is not empty, otherwise the type conversion is performed

Understand the entire process of creating beans, the most important of which is step 8, creating for different scopes. Before detailing the functions provided by each step, let's first understand the usage of FactoryBean.

Use of FactoryBeans

Spring uses the reflection mechanism to use the bean's class attribute to specify the implementation class to instantiate the bean. In some cases, the instance bean is more complex if a lot of configuration information is required in the <bean> tag and flexibility is limited.

Spring provides the factory interface org.springframework.beans.factory.FactoryBean , we can implement this interface to customize the logic of instantiating beans.

 public interface FactoryBean<T> {

   String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

     /**
     * 返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放入到Spring容器的单例缓存池中
     */
   @Nullable
   T getObject() throws Exception;

  /**
     * 返回FactoryBean创建的bean类型
     */
   @Nullable
   Class<?> getObjectType();

  /**
     * 返回FactoryBean创建的bean实例的作用域是singleton还是prototype
     */
   default boolean isSingleton() {
      return true;
   }

}

When the implementation class configured by the class attribute in the configuration file <bean> is FactoryBean, the returned object obtained by the getBean() method is not the FactoryBean itself, but the object returned by the FactoryBean#getObject() method, which is equivalent to Proxy the getBean() method.

If you configure the following Car's <bean> in the traditional way, each attribute corresponds to a <property> tag.

 /**
 * @author 神秘杰克
 * 公众号: Java菜鸟程序员
 * @date 2022/5/30
 */
public class Car {

   private int maxSpeed;
   private String brand;
   private double price;
   // 省略 get / set
}

If you use the FactoryBean method, it will be more flexible, such as assigning all the properties of Car at one time through the comma separator.

 /**
 * @author 神秘杰克
 * 公众号: Java菜鸟程序员
 * @date 2022/5/30
 */
public class CarFactoryBean implements FactoryBean<Car> {

   private String carInfo;

   @Override
   public Car getObject() throws Exception {
      Car car = new Car();
      final String[] infos = carInfo.split(",");
      car.setBrand(infos[0]);
      car.setMaxSpeed(Integer.parseInt(infos[1]));
      car.setPrice(Double.parseDouble(infos[2]));
      return car;
   }

   @Override
   public Class<?> getObjectType() {
      return Car.class;
   }

   public String getCarInfo() {
      return carInfo;
   }

   public void setCarInfo(String carInfo) {
      this.carInfo = carInfo;
   }

   @Override
   public boolean isSingleton() {
      return false;
   }

}

After you have CarFactoryBean, you can configure Car Bean using the following custom configuration in the configuration file:

 <bean id="car" class="cn.jack.CarFactoryBean">
   <property name="carInfo" value="宝马,400,20000000"/>
</bean>

When calling getBean("car") , it is found that CarFactoryBean implements the FactoryBean interface through the reflection mechanism. At this time, the Spring container calls the interface method CarFactoryBean#getObject() method to return. If you want to get an instance of CarFactoryBean, you need to prefix the beanName with "&", for example: getBean("&car") .

Get a singleton bean from the cache

After understanding the usage of FactoryBean, we can understand the process of bean loading.

We know that the singleton will only be created once in the same container of Spring, and then the bean will be obtained directly from the singleton cache. Note that it is only trying to load here.

First try loading from cache, then try loading from singletonFactories again. Because there will be dependency injection when creating singleton beans, and in order to avoid circular dependencies when creating dependencies, Spring's principle of creating beans is to expose the bean's ObjectFactory in advance and add it to the cache before the bean is created. When the next bean is created, it needs to depend on the previous bean, so use ObjectFactory directly.

 public Object getSingleton(String beanName) {
   //参数true表示允许早期依赖
   return getSingleton(beanName, true);
}
 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   //检查缓存中是否存在实例
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      //为空则加锁
      synchronized (this.singletonObjects) {
         //如果此时bean正在加载则不处理
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            //当某些方法需要提前初始化时候则调用addSingleTonFactory方法讲对应的ObjectFactory初始化策略存入singletonFactories
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               //调用预先设定的getObject方法
               singletonObject = singletonFactory.getObject();
               //记录到缓存中,注意:earlySingletonObjects和singletonFactories是互斥的
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

This method first tries to get the instance from singletonObjects, if it can't get it, get it from earlySingletonObjects, if it still can't get it, get the ObjectFactory corresponding to beanName from singletonFactories, then call getObject of ObjectFactory to create a bean, and put it in earlySingletonObjects, And remove the ObjectFactory from the singletonFactories, all subsequent memory operations are only used for circular dependency detection, that is, when allowEarlyReference is true.

Summarize

There are mentioned different maps used to store beans:

  • singletonObjects : Level 1 cache , saves the relationship between BeanName and bean instance creation, bean name --> bean instance
  • earlySingletonObjects : Level 2 cache , which saves the relationship between BeanName and bean instance creation. Unlike singletonObjects, when a singleton bean is placed here, when the bean is still in the process of being created, It can be obtained through the getBean method, the purpose is to detect circular references
  • singletonFactories : Level 3 cache , saves the relationship between BeanName and the factory that created the bean, bean name --> ObjectFactory
  • registeredSingletons : This instance is a Set object, which is used to save all currently registered beans

神秘杰克
765 声望383 粉丝

Be a good developer.