5.5  准备创建bean

  我们不可能指望在一个函数中完成一个复杂的逻辑,而且我们跟踪了这么多Spring代码,经历了这么多函数,或多或少也发现了一些规律:一个真正干活的函数其实是以do开头的,比如doGetObjectFromFactoryBean;而给我们错觉的函数,比如getObjectFromFactoryBean,其实只是从全局角度去做些统筹的工作。这个规则对于createBean也不例外,那么让我们看看在createBean函数中做了哪些准备工作。

 1 protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)    throws BeanCreationException {
 2
 3          if (logger.isDebugEnabled()) {
 4              logger.debug("Creating instance of bean '" + beanName + "'");
 5          }
 6          //锁定class,根据设置的class属性或者根据className来解析Class
 7          resolveBeanClass(mbd, beanName);
 8
 9          //验证及准备覆盖的方法
10          try {
11              mbd.prepareMethodOverrides();
12          }
13          catch (BeanDefinitionValidationException ex) {
14              throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
15                      beanName, "Validation of method overrides failed", ex);
16          }
17
18          try {
19              //给BeanPostProcessors一个机会来返回代理来替代真正的实例
20              Object bean = resolveBeforeInstantiation(beanName, mbd);
21              if (bean != null) {
22                  return bean;
23              }
24          }
25          catch (Throwable ex) {
26              throw new BeanCreationException(mbd.getResourceDescription(), beanName,
27                      "BeanPostProcessor before instantiation of bean failed", ex);
28          }
29
30          Object beanInstance = doCreateBean(beanName, mbd, args);
31          if (logger.isDebugEnabled()) {
32              logger.debug("Finished creating instance of bean '" + beanName + "'");
33          }
34          return beanInstance;
35      }

从代码中我们可以总结出函数完成的具体步骤及功能。

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

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

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

其实在Spring中确实没有override-method这样的配置,但是如果读过前面的部分,可能会有所发现,在Spring配置中是存在lookup-method和replace-method的,而这两个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性里,而这个函数的操作其实也就是针对于这两个配置的。

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

(4)创建bean。

我们首先查看下对override属性标记及验证的逻辑实现。

处理ovverride属性

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

 1 public void prepareMethodOverrides() throws BeanDefinitionValidationException {
 2          // Check that lookup methods exists.
 3          MethodOverrides methodOverrides = getMethodOverrides();
 4          if (!methodOverrides.isEmpty()) {
 5              for (MethodOverride mo : methodOverrides.getOverrides()) {
 6                  prepareMethodOverride(mo);
 7              }
 8          }
 9 }
10   protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
11      //获取对应类中对应方法名的个数
12          int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
13          if (count == 0) {
14              throw new BeanDefinitionValidationException(
15                      "Invalid method override: no method with name '" + mo.getMethodName() +
16                      "' on class [" + getBeanClassName() + "]");
17          }
18          else if (count == 1) {
19              //标记MethodOverride暂未被覆盖,避免参数类型检查的开销。
20              mo.setOverloaded(false);
21          }
22 }

  通过以上两个函数的代码你能体会到它所要实现的功能吗?之前反复提到过,在Spring配置中存在lookup-method和replace-method两个配置功能,而这两个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性里,这两个功能实现原理其实是在bean实例化的时候如果检测到存在methodOverrides属性,会动态地为当前bean生成代理并使用对应的拦截器为bean做增强处理,相关逻辑实现在bean的实例化部分详细介绍。

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

5.5.2  实例化的前置处理

  在真正调用doCreate方法创建bean的实例前使用了这样一个方法resolveBeforeInstantiation (beanName, mbd)对BeanDefinigiton中的属性做些前置处理。当然,无论其中是否有相应的逻辑实现我们都可以理解,因为真正逻辑实现前后留有处理函数也是可扩展的一种体现,但是,这并不是最重要的,在函数中还提供了一个短路判断,这才是最为关键的部分。

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

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

 1 protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
 2          Object bean = null;
 3 //如果尚未被解析
 4          if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
 5              // Make sure bean class is actually resolved at this point.
 6              if (mbd.hasBeanClass() && !mbd.isSynthetic() && hasInstantiationAware BeanPostProcessors()) {
 7                  bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName);
 8                  if (bean != null) {
 9                      bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
10                  }
11              }
12              mbd.beforeInstantiationResolved = (bean != null);
13          }
14          return bean;
15      }

  此方法中最吸引我们的无疑是两个方法applyBeanPostProcessorsBeforeInstantiation以及applyBeanPostProcessorsAfterInitialization。两个方法实现的非常简单,无非是对后处理器中的所有InstantiationAwareBeanPostProcessor类型的后处理器进行postProcessBeforeInstantiation方法和BeanPostProcessor的postProcessAfterInitialization方法的调用。

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

  bean的实例化前调用,也就是将AbsractBeanDefinition转换为BeanWrapper 前的处理。给子类一个修改BeanDefinition的机会,也就是说当程序经过这个方法后,bean可能已经不是我们认为的bean了,而是或许成为了一个经过处理的代理bean,可能是通过cglib生成的,也可能是通过其它技术生成的。这在第7章中会详细介绍,我们只需要知道,在bean的实例化前会调用后处理器的方法进行处理。

 1   protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName)
 2              throws BeansException {
 3
 4          for (BeanPostProcessor bp : getBeanPostProcessors()) {
 5              if (bp instanceof InstantiationAwareBeanPostProcessor) {
 6                  InstantiationAwareBeanPostProcessor ibp = (Instantiation AwareBean PostProcessor) bp;
 7                  Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
 8                  if (result != null) {
 9                      return result;
10                  }
11              }
12          }
13          return null;
14      }

2.实例化后的后处理器应用

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

 1 public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
 2              throws BeansException {
 3
 4          Object result = existingBean;
 5          for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
 6              result = beanProcessor.postProcessAfterInitialization(result, beanName);
 7              if (result == null) {
 8                  return result;
 9              }
10          }
11          return result;
12      } 

5.5 准备创建bean相关推荐

  1. Spring注解创建Bean的几种方式

    Spring注解创建Bean的几种方式 1.@Component系列 @Component @Service @Repository @Controller @Configuration 2. 依附于 ...

  2. Spring 学习总结 使用静态工厂创建Bean

    创建Bean时,class属性必须指定,此时为静态工厂类. factory-method指定静态工厂方法名. 接口: 1 2 3 public interface Being {     public ...

  3. java bean 工厂模式_Spring框架通过工厂创建Bean的三种方式实现

    工厂模式 Spring中bean的创建,默认是框架利用反射new出来的bean实例.有时候也会有一些复杂的情况. 假设有一个飞机,属性如下,现在需要造很多同型号的飞机,那么唯一需要改变的属性只有Dri ...

  4. 把对象的创建交给spring来管理——  1.创建bean的三种方式     2.bean对象的作用范围     3.bean对象的生命周期

    把对象的创建交给spring来管理 spring对bean的管理细节     1.创建bean的三种方式     2.bean对象的作用范围     3.bean对象的生命周期 创建Bean的三种方式 ...

  5. Spring创建Bean的3种方式

    1. Spring创建Bean的三种方式 1. 调用构造器(因为常用就不讲) 2. 调用静态工厂方法 3. 调用实例工厂方法 2. 使用静态工厂方法 1.此时<bean.../>元素要指定 ...

  6. java动态创建bean的意义_java相关:Spring Boot如何动态创建Bean示例代码

    java相关:Spring Boot如何动态创建Bean示例代码 发布于 2020-3-20| 复制链接 摘记: 前言本文主要给大家介绍了关于Spring Boot动态创建Bean的相关内容,分享出来 ...

  7. spring中bean的细节之三种创建Bean对象的方式

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  8. 五、创建Bean的三种方式

    五.创建Bean的三种方式 转载于:https://www.cnblogs.com/ljiwej/p/7280614.html

  9. 使用CommandLineRunner或ApplicationRunner接口创建bean

    在spring boot应用中,我们可以在程序启动之前执行任何任务.为了达到这个目的,我们需要使用CommandLineRunner或ApplicationRunner接口创建bean,spring ...

最新文章

  1. 如何改变iframe滚动条的样式?
  2. oracle 数据库里查看表空间使用状况
  3. 子串字谜substring anagrams
  4. php 数组 双向链表,一个字节数组双向链表类,主要针对串口通讯而开发的
  5. nioqrc oracle,程序停在 readnocancel () from -lib-tls-libpthread.so.0
  6. idea展示runDashboard的窗口
  7. 计算机程序的思维逻辑 (54) - 剖析Collections - 设计模式
  8. 校园卡管理系统-版本二
  9. 软件测试分析流程及输出项包括哪些内容?
  10. 导数与微分及简单例题
  11. 计算机导论黄国兴百度云,计算机导论黄国兴
  12. 下面关于在dos停止mysql_下面关于在DOS停止MySql的命令中,正确的是 (5.0分)_学小易找答案...
  13. 追踪货拉拉:“亡羊补牢”进度缓慢,安全“地雷”何时能拆完?
  14. MATLAB_tool使用心得
  15. 计算机组用户名跟密码忘了,访问局域网工作组的电脑需要用户名和密码怎么办...
  16. ChatGPT实现编程语言转换
  17. C语言 数字与字符/字符串的相互转换
  18. Paxos - 分布式表决算法
  19. 学习生物信息学的理由
  20. 长牌 算法 算包(不包含癞子)

热门文章

  1. React setStats数组不更新,百思不得其解。
  2. 【nuxtjs 指南】解决nuxtjs本地开发跨域和防止路由与api冲突问题
  3. JS---------正则表达式
  4. 怎样实现MathType中带箭头向量的输入
  5. Some projects cannot be imported because they already exist in the workspace
  6. 教育行业的互联网焦虑症
  7. 怎样判断ios app 第一次启动
  8. H3C 5510 交换机DHCP设置
  9. ASP语言基础之常量的定义方法
  10. 利用正则表达式截取特定字符中间字符