• Spring 源码(11)Spring Bean 的创建过程(2)


    Spring Bean 的创建过程介绍了FactoryBean 的创建方式,那么接下来介绍不是FactoryBean的创建方式,在创建过程中,又会分为单例的Bean的创建,原型类型的Bean的创建等。一般来说在Spring中几乎所有对象都是单例创建的,除非有其他业务需要设置为其他作用域的Bean,所以重点以创建单例Bean为例。

    单例Bean的创建

    在创建时会调用getBean,然后doGetBean,一般来说在Spring中只要是do开头方法基本就是真正干活的方法,所以我们看doGetBean方法的源码:

    protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {
      // 解析成规范的Bean name ,因为可能是FactoryBean加了& 前缀的Bean或者是有别名的Bean
      String beanName = transformedBeanName(name);
      Object bean;
      // Eagerly check singleton cache for manually registered singletons.
      // 获取缓存中的Bean
      Object sharedInstance = getSingleton(beanName);
      if (sharedInstance != null && args == null) {
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
      }
      // 如果缓存中没有,那么就会按照单例或者多例的方式创建
      else {
        // 省略代码....
    
        // Check if bean definition exists in this factory.
        // 检查父类容器
        // 省略代码....
    
        if (!typeCheckOnly) {
          // 标记已经被创建
          markBeanAsCreated(beanName);
        }
    
        try {
          // 合并BeanDefinition
          RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
          checkMergedBeanDefinition(mbd, beanName, args);
    
          // Guarantee initialization of beans that the current bean depends on.
          // 判断是否存在依赖的Bean的创建,比如dependsOn 依赖 A 这个Bean,那么就需要先创建A这个bean
          String[] dependsOn = mbd.getDependsOn();
          if (dependsOn != null) {
            for (String dep : dependsOn) {
              if (isDependent(beanName, dep)) {
                // 省略代码....
              }
              // 注册依赖的Bean,放在集合中
              registerDependentBean(dep, beanName);
              try {
                // 创建Bean
                getBean(dep);
              }
              catch (NoSuchBeanDefinitionException ex) {
               // 省略代码....
              }
            }
          }
    
          // Create bean instance.
          if (mbd.isSingleton()) {
            // 如果是单例的,就去创建Bean
            sharedInstance = getSingleton(beanName, () -> {
              try {
                // 创建Bean
                return createBean(beanName, mbd, args);
              }
              catch (BeansException ex) {
                // 省略代码....
              }
            });
            // 获取bean对象,会进行检查获取对象是否是FactoryBean
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
          }
          // 原型作用的创建方式
          else if (mbd.isPrototype()) {
           // 省略代码....
          }
          else {
            // 省略代码....
          }
        }
        catch (BeansException ex) {
          // 省略代码....
        }
      }
      // 省略代码....
      return (T) bean;
    }
    

    去掉不重要的代码,可以看到首先是从缓存中获取,如果没有获取到就进行一些列检查,最终检查是否单例的Bean,如果是,那么就会调用getSingleton方法,传入一个beanName,一个ObjectFactorylambda表达式,表达式中有个createBean方法,这个方法就是创建的Bean方法。

    那什么时候调用crateBean方法呢?

    答案是执行lambda表达式的具体方法时执行,我们先看看这个ObjectFactory接口是啥?

    ObjectFactory 对象工厂

    直接看源码:

    @FunctionalInterface
    public interface ObjectFactory<T> {
    
    	/**
    	 * Return an instance (possibly shared or independent)
    	 * of the object managed by this factory.
    	 * @return the resulting instance
    	 * @throws BeansException in case of creation errors
    	 */
    	T getObject() throws BeansException;
    
    }
    

    这个接口是一个函数式接口,可以用于lambda表达式直接使用,在调用getObject方法时就是真正执行lambda表达式中的方法。

    具体看看getSingleton方法的源码:

    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
      Assert.notNull(beanName, "Bean name must not be null");
      synchronized (this.singletonObjects) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
          if (this.singletonsCurrentlyInDestruction) {
            // 省略代码....
          }
          // 省略代码....
          // 检查 并添加正在创建的单例对象到集合中
          beforeSingletonCreation(beanName);
          // 设置为新的单例对象标识
          boolean newSingleton = false;
          // 设置异常集合,出现异常时将异常加入到集合中
          boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
          if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
          }
          try {
            // 执行具体的方法,调用crateBean方法
            singletonObject = singletonFactory.getObject();
            // 标识为新的单例对象
            newSingleton = true;
          }
          catch (IllegalStateException ex) {
            // 省略代码....
          }
          catch (BeanCreationException ex) {
            // 省略代码....
          }
          finally {
            if (recordSuppressedExceptions) {
              this.suppressedExceptions = null;
            }
            // 检查 并移除正在创建的单例对象
            afterSingletonCreation(beanName);
          }
          if (newSingleton) {
            // 加入到缓存中
            addSingleton(beanName, singletonObject);
          }
        }
        return singletonObject;
      }
    

    这里执行完之后就会执行到lambda表达式中的createBean方法:

    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
          throws BeanCreationException {
    
       // 省略代码....
       RootBeanDefinition mbdToUse = mbd;
    
       // Make sure bean class is actually resolved at this point, and
       // clone the bean definition in case of a dynamically resolved Class
       // which cannot be stored in the shared merged bean definition.
       // 解析Bean的Class 用于反射创建对象
       Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
       if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
          mbdToUse = new RootBeanDefinition(mbd);
          mbdToUse.setBeanClass(resolvedClass);
       }
    
       // Prepare method overrides.
       try {
          // 方法覆盖准备 lookup-method replace-method
          mbdToUse.prepareMethodOverrides();
       }
       catch (BeanDefinitionValidationException ex) {
          // 省略代码....
       }
    
       try {
          // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
          // 解析提前实例化,使用InstantiationAwareBeanPostProcessor实现
          Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
          if (bean != null) {
             return bean;
          }
       }
       catch (Throwable ex) {
          // 省略代码....
       }
    
       try {
          // 实例化 + 初始化 Bean
          // 真正的创建Bean
          Object beanInstance = doCreateBean(beanName, mbdToUse, args);
          // 省略代码....
          return beanInstance;
       }
       catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
          // 省略代码....
       }
       catch (Throwable ex) {
          // 省略代码....
       }
    }
    

    首先是进行了Bean的类型的解析,主要是用于后面的反射创建对象时使用,并设置到RootBeanDefinition中,然后进行方法覆盖操作。

    Spring方法覆盖实战

    方法覆盖就是使用了lookup-methodreplace-method 标签的时候,就会进行方法的覆盖。方法覆盖有什么用处呢?

    一般来说方法覆盖就是解决单例对象引用多例对象的时候使用方法覆盖。

    做个实验试试:

    定义一个房子类,用于停车

    /**
     * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
     * @since 1.0
     **/
    public abstract class MyHouse {
    
    	public abstract MyCar park();
    
    }
    
    

    定义我的车

    /**
     * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
     * @since 1.0
     **/
    public interface MyCar {
    	/**
    	 * 买一辆车
    	 * @return 车
    	 */
    	MyCar buy();
    }
    

    定义实现类:

    宝马车:

    /**
     * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
     * @since 1.0
     **/
    public class BMW implements MyCar{
    
    	@Override
    	public MyCar buy() {
    		return this;
    	}
    
    }
    

    奔驰车:

    /**
     * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
     * @since 1.0
     **/
    public class Ben implements MyCar{
    	@Override
    	public MyCar buy() {
    		return this;
    	}
    
    }
    

    xml配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	   xmlns:context="http://www.springframework.org/schema/context"
    	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
    	<bean id="myHouse1" class="com.redwinter.test.methodoverride.lookupmethod.MyHouse" >
    		<lookup-method name="park" bean="bmw"/>
    	</bean>
    	<bean id="myHouse2" class="com.redwinter.test.methodoverride.lookupmethod.MyHouse" >
    		<lookup-method name="park" bean="ben"/>
    	</bean>
       <!-- 设置为原型-->
    	<bean id="bmw" class="com.redwinter.test.methodoverride.lookupmethod.BMW" scope="prototype"/>
    	<bean id="ben" class="com.redwinter.test.methodoverride.lookupmethod.Ben"/>
    </beans>
    

    测试类:

    /**
     * @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
     * @since 1.0
     **/
    public class LookupTest {
    
    	/**
    	 * lookup-method 用来解决单例对象多例对象的
    	 */
    	@Test
    	public void lookupTest(){
    		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:method-override.xml");
    		MyHouse myHouse = (MyHouse) context.getBean("myHouse1");
    		MyHouse myHouse1 = (MyHouse) context.getBean("myHouse1");
    		System.out.println(myHouse.park());
    		System.out.println(myHouse1.park());
    	}
    }
    

    输出:

    com.redwinter.test.methodoverride.lookupmethod.BMW@4a765
    com.redwinter.test.methodoverride.lookupmethod.BMW@3e6358
    

    这里Myhouse是一个单例的对象,myHouse1 调用的方法每次调用都是不同的对象。

    Spring是如何实现方法覆盖的?

    源码过于繁琐和复杂,这里直接看执行流程:

    Spring在加载BeanDefinition的时候,执行 parseLookupOverrideSubElements 这个方法的时候只要设置了lookup-method标签就会创建一个LookupOverride类放入到BeanDefinitionMethodOverrides 属性中,在进行Bean的创建的时候,就会判断这个属性值是否有值,如果有那么就会在对象实例化时获取一个实例化策略,然后执行实例化,就、就会调用SimpleInstantiationStrategy#instantiate 方法,然后使用CGLIB进行实例化,创建出一个Enhancer 增强类,并且设置一个回调类型为:

    private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
    				{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
    

    最终在执行方法的时候就会调用到回调类LookupOverrideMethodInterceptor 拦截器上,然后执行Bean的创建:

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
      // Cast is safe, as CallbackFilter filters are used selectively.
      LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
      Assert.state(lo != null, "LookupOverride not found");
      Object[] argsToUse = (args.length > 0 ? args : null);  // if no-arg, don't insist on args at all
      if (StringUtils.hasText(lo.getBeanName())) {
        // 创建Bean
        Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
                       this.owner.getBean(lo.getBeanName()));
        // Detect package-protected NullBean instance through equals(null) check
        return (bean.equals(null) ? null : bean);
      }
      else {
        return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
                this.owner.getBean(method.getReturnType()));
      }
    }
    

    这篇文章就介绍到这里,下一篇继续。

  • 相关阅读:
    谷粒商城实战(044集群学习-redis集群)
    前端系列-1 HTML+JS+CSS基础
    spring源码 - 理解@Import原理及运用
    6.1 - 6.2 公钥密码学简介
    PerfView专题 (第三篇):如何寻找 C# 中的 VirtualAlloc 内存泄漏
    R语言根据分隔符分割字符串:使用strsplit函数基于指定分隔符将字符串进行分割(返回的结果为列表)
    视频去水印 部分源码(包含部分php与go)有需要可以联系我
    springboot系列(二十):如何通过redis实现手机号验证码功能 |超级详细,建议收藏
    java计算机毕业设计人才招聘系统智能化管理源码+mysql数据库+系统+lw文档+部署
    Flink CDC引起的Mysql元数据锁
  • 原文地址:https://www.cnblogs.com/redwinter/p/16255297.html