zoukankan      html  css  js  c++  java
  • 准备创建bean

    准备创建bean

    经过了查看那么多的源码,我们或多或少的能发现一些规律,比如:一个函数中不可能完成所有的逻辑,一个真正干活的函数是以do开头的,例如doGetObjectFromFactoryBean。这篇文章接着上篇文章获取单例讲述,所以先来看看createBean函数中做了哪些准备工作:

    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
                throws BeanCreationException {
    
            if (logger.isTraceEnabled()) {
                logger.trace("Creating instance of bean '" + beanName + "'");
            }
            RootBeanDefinition mbdToUse = mbd;
    
            //锁定class根据设置的class属性或者根据className来解析class
            Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
            if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
                mbdToUse = new RootBeanDefinition(mbd);
                mbdToUse.setBeanClass(resolvedClass);
            }
    
            // 验证及准备覆盖的方法
            try {
                mbdToUse.prepareMethodOverrides();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                        beanName, "Validation of method overrides failed", ex);
            }
    
            try {
                // 给BeanPostProcessors一个机会来返回代理来替代真正的实例
                Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
                if (bean != null) {
                    return bean;
                }
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                        "BeanPostProcessor before instantiation of bean failed", ex);
            }
    
            try {
                Object beanInstance = doCreateBean(beanName, mbdToUse, args);
                if (logger.isTraceEnabled()) {
                    logger.trace("Finished creating instance of bean '" + beanName + "'");
                }
                return beanInstance;
            }
            catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
                throw ex;
            }
            catch (Throwable ex) {
                throw new BeanCreationException(
                        mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
            }
        }

     从上述代码中可以总结出函数完成的具体步骤及功能:

    (1)根据设置的class属性或者根据className来解析Class;

    (2)对override属性进行标记及验证;

      这里的话可能会不知道这个方法的作用,因为在Spring的配置里面根本就没有诸如override-method之类的配置,那么这个方法到底是干什么用的呢?

        其实在Spring中确实没有override-method这样的配置,但是在Spring配置中国是存在lookup-method和replace-method的,而这两个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性里,而这个函数的操作其实就是针对这两个配置的。

    (3)应用初始化前的后处理器,解析指定bean是否存在初始化前的短路操作;

    (4)创建bean。

    处理override属性

    查看源码中AbstractBeanDefinition类的prepareMethodOverrides方法:

    public void prepareMethodOverrides() throws BeanDefinitionValidationException {
            // Check that lookup methods exists.
            if (hasMethodOverrides()) {
                Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
                synchronized (overrides) {
                    for (MethodOverride mo : overrides) {
                        prepareMethodOverride(mo);
                    }
                }
            }
        }
    protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
            //获取对应类中对应方法名的个数
            int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
            if (count == 0) {
                throw new BeanDefinitionValidationException(
                        "Invalid method override: no method with name '" + mo.getMethodName() +
                        "' on class [" + getBeanClassName() + "]");
            }
            else if (count == 1) {
                //标记MethodOverride暂未被覆盖,避免参数类型检查的开销
                mo.setOverloaded(false);
            }
        }

     从上述代码中可以发现,我们并不能从代码中体会到这两个方法所要实现的功能。之前不止一次的提到过这样一句话:在Spring配置中存在lookup-method和replace-method两个配置功能,而这两个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性里,这两个功能的实现原理其实是在bean实例化的时候如果检测到存在methodOverrides属性,会动态的为当前bean生成代理并使用对应的拦截器为bean做增强处理,相关的逻辑实现会在以后bean的实例化会详细讲述。

      但是,这里要强调的是,对于方法的匹配来讲,如果一个类中存在若干个重载方法,那么在函数调用及增强的时候还需要根据参数类型进行匹配,来最终确认当前调用的到底是哪个函数。但是,Spring将一部分匹配工作在这里完成了,如果当前类中的方法只有一个,那么就设置重载该方法没有被重载,这样在后续调用的时候便可以直接使用找到的方法,而不需要进行方法的参数匹配验证了,而且还可以提前对方法存在性进行验证,可谓一箭双雕。

    实例化的前置处理

    在真正调用doCreate方法创建bean的实例前使用了这样一个方法resolveBeforeInstantiation(beanName,mbd)对BeanDefinition中的属性做些前置处理。在这个createBean函数中,提供了一个短路判断,这是很关键的一部分:

    if(bean != null){
      return bean;  
    }

    当经过前置处理后如果返回的结果不为空,那么会直接略过后续的bean的创建而直接返回结果。这一特性虽然很容易被忽略,但是却起着至关重要的作用,我们熟知的Spring的AOP功能就是基于这里的判断的。来看一下这个前置处理的源代码:

    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
            Object bean = null;
            //如果尚未被解析
            if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
                if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                    Class<?> targetType = determineTargetType(beanName, mbd);
                    if (targetType != null) {
                        bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                        if (bean != null) {
                            bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                        }
                    }
                }
                mbd.beforeInstantiationResolved = (bean != null);
            }
            return bean;
        }

    此段代码中最吸引我们的无非就两个方法:applyBeanPostProcessorsBeforeInstantiation和applyBeanPostProcessorsAfterInitialization,这两个方法分别是实例化前的后处理器应用和实例化后的后处理器应用。我们分别来对其进行讲述:

    (1)实例化前的后处理器应用

      bean的实例化前调用,也就是将AbstractBeanDefinition转换为BeanWrapper前的处理。给子类一个修改BeanDefinition的机会,也就是说当程序经过这个方法后,bean可能已经不是我们认为的bean了,而是或许成为了一个经过处理的代理bean,可能是通过cglib生成的,也可能是通过其他技术生成的。

    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                    if (result != null) {
                        return result;
                    }
                }
            }
            return null;
        }

     (2)实例化后的后处理器应用

      在前面的文章从缓存中获取单例bean的时候就提到过,Spring中的规则是在bean的初始化后尽可能的保证将注册后的处理器的postProcessAfterInitialization方法应用到该bean中,因为如果返回的bean不为空,那么便不会再次经历普通bean的创建过程,所以只能在这里应用后处理器的postProcessAfterInitialization方法。

    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
                throws BeansException {
    
            Object result = existingBean;
            for (BeanPostProcessor processor : getBeanPostProcessors()) {
                Object current = processor.postProcessAfterInitialization(result, beanName);
                if (current == null) {
                    return result;
                }
                result = current;
            }
            return result;
        }

     参考:《Spring源码深度解析》 郝佳 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    在pos:a元素不设定宽度的情况下,他的最大宽度是受父元素的宽度所限制的。
    跳过权限检查,强制修改mysql密码
    IIS7.5 配置ASP+ACCESS使用环境(转)
    windows 2008配置运行PHP5.5.X
    Content encoding error问题解决方法
    ubuntu 中文显示乱码问题 (转)
    Ubuntu 12.04中文输入法的安装(zhuan)
    html5开发之viewport使用
    Windows/Linux 环境搭建Git服务器 + vs2012集成git
    Windows+VS+SVN实现版本控制
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10149837.html
Copyright © 2011-2022 走看看