写点什么

Spring Bean 创建过程的 Hook

用户头像
邱学喆
关注
发布于: 2021 年 04 月 17 日
Spring Bean创建过程的Hook

一. 概述

在《spring 的 IOC 使用以及原理》要讲述到 bean 的创建过程,但并没有涉及到创建 Bean 过程的 Hook。所谓 Hook,就是在创建 Bean 过程中提供接口,该接口可以影响创建 Bean 的过程。在 Spring 中的 Hook 是 BeanPostProcessor 接口,其下还有一些子接口。类图继承关系如下:

里面有两个单词概念需要澄清的,Instantiation 与 Initialization。Instantiation 是实例化,即 Bean 的创建。Initialization 是初始化,即对 Bean 的初始化,主要是属性的初始化。

  • BeanPostProcessor 有关针对 Bean 的初始化前后操作接口

  • InstantiationAwareBeanPostProcessor 有关针对 Bean 的实例化前后的操作接口

  • SmartInstantiationAwareBeanPostProcessor 有关预测 Bean 的最终类型,一般是用来做 AOP 的过程。

  • MergedBeanDefinitionPostProcessor 用来合并 BeanDefinition 对象,主要是对 BeanDefinition 的修改添加额外的信息

  • DesctructionAwareBeanPostProcessor 添加

二. Hook

  • BeanPostProcessor

  • postProcessBeforeInitialization Bean 的初始化前调用该方法。这里的初始化主要是指 InitializitingBean.afterPropertiesSet 方法,自定义 init-method 方法。具体的代码逻辑在 AbstractAutowireCapableBeanFactory.initializeBean 方法。

  • postProcessAfterInitialization Bean 的初始化后调用该方法。这里的初始化主要是指 InitializitingBean.afterPropertiesSet 方法,自定义 init-method 方法。具体的代码逻辑在 AbstractAutowireCapableBeanFactory.initializeBean 方法。但也有例外,当调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation 返回一个不为空的 Bean 时,会直接该方法,接着就直接返回

  • InstantiationAwareBeanPostProcessor

  • postProcessBeforeInstantiation 实例化前调用该方法,该方法一般是用来创建目标对象的代理实例。当返回的 bean 不为空,会接着调用 BeanPostProcessor.postProcessAfterInitialization 方法进行初始化。接着就结束整个 bean 的创建过程。这个回调方法将仅仅运用到真实类型,而不是工厂方法,因为工厂方法创建会 Bean 是动态的,而回调方法是静态的。主要的实现类是 AbstractAutoProxyCreator 的子类。

  • postProcessAfterInstantiation 实例化后,spring 属性填充(population)动作之前回调该方法。当返回 false 时,则无需进行属性的初始化,如 BeanDefinition.propertyValues 注入以及 autowiring 属性注入。当返回 true 时,属性则应该注入到对象当中。默认的返回 true。目前没有子类是返回 false 的。

  • postProcessPropertyValues 在容器对 Bean 的属性注入时前对提供的属性的值进行处理。一般是用来检查对象 Bean 的依赖是否满足,例如在 Bean 中含有 @Required 注解的属性。也可以用来替换 BeanDefinition.propertyValues 的对象,从而达到修改属性的效果。如子类 RequiredAnnotationBeanPostProcessor 对包含 @Required 注解的属性进行校验,是否满足。另外,有关 @Autowired 以及 @Resource 注解的属性的注入,其实现类分别是 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor

  • SmartInstantiationAwareBeanPostProcessor

  • predictBeanType 预测从 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation 回调方法返回的最终类型。

  • determineCandidateConstructors 决定指定的类中的候选构造方法,从而影响容器中的 Bean 的创建方式。

  • getEarlyBeanReference 对于早期访问指定的 Bean 而暴露引用,通常是为了解决循环问题。在 Bean 完全初始化之前提供一种机制,先暴露出 Bean 的封装。常规情况下,暴露出来的 Bean 是不变的。当暴露的 Bean 是与之前的 Bean 不一样时,这意味者对原始 Bean 进行了串改,返回其他类型。则需要暴露出一个方法给 spring 容器调用,去获取最终的类型,则就是 predictBeanType 的由来。通常情况下,该方法返回的是原始 Bean。AOP 代理类通常是在这个方法下进行对原始 Bean 的串改,实现类为 AbstractAutoProxyCreator 的子类

  • MergedBeanDefinitionPostProcessor

  • postProcessMergedBeanDefinition 对指定的 Bean 的 BeanDefinition 进行合并操作,从而达到修改 BeanDefinition 的效果,从而影响 Bean 的创建过程。AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 实现就会将含有 @Autowired 和 @Resource 注解的属性保存到 BeanDefinition 中的 externallyManagedConfigMembers。目前没有找到针对这个 externallyManagedConfigMembers 属性进一步处理的代码。

  • DestructionAwareBeanPostProcessor 常规的实现类是 InitDestroyAnnotationBeanPostProcessor

  • postProcessBeforeDestruction 针对 Bean 销毁前调用的方法自定义销毁方法 @PreDestroy 标注的方法。

  • requiresDestruction 决定指定的实例 Bean 是否需要被销毁.

为了对以上的 HOOK 在 Spring 的 bean 过程被调用,画了粗糙的流程图,便以进一步理解以及掌握。


三. Hook 的实现类


  • ServletContextAwareProcessor 向实现 ServletContextAware 和 ServletConfigAware 接口的 Bean 的注入 ServletContext 和 ServletConfig 的属性

  • DefaultAdvisorAdapterRegistry 检测 spring 容器的 Bean,如果该 Bean 是 AdvisorAdapter 的实现类,则将其 Bean 注册到全局 Advisor 适配器对象中,提供给 AOP 信息

  • MethodValidationPostProcessor AOP 切面,针对方法中的返回参数以及入参的对象校验,当对象中包含了 @Validated 注解,会对方法中的对象进行约束校验。

  • AsyncAnnotationBeanPostProcessor 针对含有 @Async 注解标注的方法,进行切面,异步执行标注的方法;注意的是,@Async 标注的方法返回的类型是 CompletableFuture,Future 对象,这样子可以拿到异步执行后的结果,这个是属于阻塞型的;如果无需关心返回的结果,则返回的类型 Void;当然可以响应性编程,返回的类型为 ListenableFuture。

  • PersistenceExceptionTranslationPostProcessor 对类中有标注 @Repository 注解,进行拦截持久化异常处理;

  • BeanValidationPostProcessor 对 Bean 对象进行完整性校验

  • DataSourceInitializerPostProcessor 确保 DataSource 初始化后 DataSourceInitializer 也被初始化。主要是有关数据源的表以及数据的初始化。

  • ConfigurationPropertiesBindingPostProcessor 有关针对标注有 @ConfigurationProperties 注解的 Bean 对象,进行属性绑定。

  • ApplicationContextAwareProcessor 针对 EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware 等接口的实现类 Bean,进行方法调用

  • CommonAnnotationBeanPostProcessor 有关对 @Resource 注解进行属性注入的处理器

  • RequiredAnnotationBeanPostProcessor 有关对 @Required 注解依赖性校验的处理器

  • ScriptFactoryPostProcessor 有关动态脚本方面的处理器,这个不是很懂;先标志一下;

  • AutowiredAnnotationBeanPostProcessor 有关 @Autowired、@Value 注解标注的属性和方法进行注入的处理器

  • ImportAwareBeanPostProcessor 有关 ImportAware 接口实现 bean 的方法调用;

  • AbstractAutoProxyCreator 抽象类,有关创建 AOP 代理对象的处理器

  • ScheduledAnnotationBeanPostProcessor 有关处理 @Scheduled、@Schedules 注解标注的方法,将其方法放到线程定时执行;需要 @EnableScheduling 才能将其处理器注入到 spring 容器内;

四. 运用

我们基于 spring 开发过程,通常都会在属性标注 @Autowired 注解、@Resource 注解、@Value 注解,从而 Spring 自动的从容器里找到对应的对象并注入进去。

1. @Autowired

AutowiredAnnotationBeanPostProcessor 是 MergedBeanDefinitionPostProcessor 和 SmartInstantiationAwareBeanPostProcessor 接口的实现类。专门解析 Bean 对象中包含 @Autowired 注解、@Value 注解的属性和方法,然后进行对象注入。其覆盖的方法如下,并进行代码解读

public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {  //从该对象中抽取含有@Value和@Autowired注解的属性以及setter方法,封装InjectedElement,  //接着再封装InjectionMetadata对象  InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);  //将其Member对象保存到BeanDefinition.externallyManagedConfigMembers中去  metadata.checkConfigMembers(beanDefinition);}public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)  throws BeanCreationException {  // Let's check for lookup methods here..  if (!this.lookupMethodsChecked.contains(beanName)) {    try {      //检测声明的方法中含有的@Lookup注解的方法,将其信息保存到methodOverrides中去。      //后面会根据methodOverrides数量是否为空,从而影响Bean的创建      ReflectionUtils.doWithMethods(beanClass, method -> {        Lookup lookup = method.getAnnotation(Lookup.class);        if (lookup != null) {          Assert.state(beanFactory != null, "No BeanFactory available");          LookupOverride override = new LookupOverride(method, lookup.value());          try {            RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);            mbd.getMethodOverrides().addOverride(override);          }          catch (NoSuchBeanDefinitionException ex) {            throw new BeanCreationException(beanName,                                           "Cannot apply @Lookup to beans without corresponding bean definition");          }        }      });    }    catch (IllegalStateException ex) {      throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);    }    this.lookupMethodsChecked.add(beanName);  }    // Quick check on the concurrent map first, with minimal locking.  //真正的候选构造方法里列表  Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);  if (candidateConstructors == null) {    // Fully synchronized resolution now...    synchronized (this.candidateConstructorsCache) {      candidateConstructors = this.candidateConstructorsCache.get(beanClass);      if (candidateConstructors == null) {        Constructor<?>[] rawCandidates;        try {          //获取该类的构造方法列表          rawCandidates = beanClass.getDeclaredConstructors();        }        catch (Throwable ex) {          throw new BeanCreationException(beanName,                                                                                   "Resolution of declared constructors on bean Class [" + beanClass.getName() +                                          "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);        }        //构造方法候选列表,主要是保存者@Autowired、@Value注解的构造方法列表        List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);        //含有@Autowired且Required为true的构造方法,只允许一个,多一个就会报错        Constructor<?> requiredConstructor = null;        //无参构造方法        Constructor<?> defaultConstructor = null;        //Kotlin编程的主要构造函数,在我们java开发中,是为null        Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);        int nonSyntheticConstructors = 0;        for (Constructor<?> candidate : rawCandidates) {          if (!candidate.isSynthetic()) {            //不是人造的,则+1            nonSyntheticConstructors++;          }          else if (primaryConstructor != null) {            continue;          }          //找出含有@Autowired注解以及@Value的注解的信息。          AnnotationAttributes ann = findAutowiredAnnotation(candidate);          if (ann == null) {            Class<?> userClass = ClassUtils.getUserClass(beanClass);            if (userClass != beanClass) {              try {                Constructor<?> superCtor =                  userClass.getDeclaredConstructor(candidate.getParameterTypes());                ann = findAutowiredAnnotation(superCtor);              }              catch (NoSuchMethodException ex) {                // Simply proceed, no equivalent superclass constructor found...              }            }          }          if (ann != null) {            if (requiredConstructor != null) {              throw new BeanCreationException(beanName,                                              "Invalid autowire-marked constructor: " + candidate +                                              ". Found constructor with 'required' Autowired annotation already: " +                                              requiredConstructor);            }            //检测@Autowired注解的required是否为true            boolean required = determineRequiredStatus(ann);            if (required) {              if (!candidates.isEmpty()) {                throw new BeanCreationException(beanName,                                                "Invalid autowire-marked constructors: " + candidates +                                                ". Found constructor with 'required' Autowired annotation: " +                                                candidate);              }              requiredConstructor = candidate;            }            candidates.add(candidate);          }          else if (candidate.getParameterCount() == 0) {            defaultConstructor = candidate;          }        }        if (!candidates.isEmpty()) {          // Add default constructor to list of optional constructors, as fallback.          if (requiredConstructor == null) {            if (defaultConstructor != null) {              candidates.add(defaultConstructor);            }            else if (candidates.size() == 1 && logger.isWarnEnabled()) {              logger.warn("Inconsistent constructor declaration on bean with name '" + beanName +                          "': single autowire-marked constructor flagged as optional - " +                          "this constructor is effectively required since there is no " +                          "default constructor to fall back to: " + candidates.get(0));            }          }          candidateConstructors = candidates.toArray(new Constructor<?>[0]);        }        else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {          //如果没有@Autowired、@Value标注的构造方法,且原生的构造方法数量只有一个,且不是无参构造方法          candidateConstructors = new Constructor<?>[] {rawCandidates[0]};        }        else if (nonSyntheticConstructors == 2 && primaryConstructor != null                 && defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) {          candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor};        }        else if (nonSyntheticConstructors == 1 && primaryConstructor != null) {          candidateConstructors = new Constructor<?>[] {primaryConstructor};        }        else {          candidateConstructors = new Constructor<?>[0];        }        this.candidateConstructorsCache.put(beanClass, candidateConstructors);      }    }  }  return (candidateConstructors.length > 0 ? candidateConstructors : null);}
复制代码

determineCandidateConstructors 方法找出构造方法的条件逻辑比较多,我们基于不是 Kolin 编程的前提下进行梳理以及总结,主要从构造方法列表中找出含有 @Autowired、@Value 注解的构造方法列表,同时如果 Required 为 false 时,则会将无参构造方法加入进来。如果没有 @Autowired、@Value 注解的构造方法,且只有一个构造有参构造方法,则返回该有参构造方法。

/** * 对含有@Autowired、@Value注解的属性以及Setter方法,从spring容器中找到对应的对象进行注入 */public PropertyValues postProcessPropertyValues(			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {  //找到该类中含有@Autowired、@Value注解的属性以及Setter方法。封装成InjectionMetadata对象		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);  try {    //接着进行对象注入    metadata.inject(bean, beanName, pvs);  }  catch (BeanCreationException ex) {    throw ex;  }  catch (Throwable ex) {    throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);  }  return pvs;}
复制代码

有关对象的注入,逻辑比较简单,而且spring 的 IOC 使用以及原理以及类型转换也又简单讲过,里面稍微多了一些逻辑,这里不再过多讲解。至于从 spring 中找出对应的对象,逻辑比较复杂,将会有专门的一篇进行讲解。

2. @Resource

CommonAnnotationBeanPostProcessor 类是 InstantiationAwareBeanPostProcessor,DestructionAwareBeanPostProcessor,MergedBeanDefinitionPostProcessor 的实现类,其主要是对 @Resource 注解进行对象注入。其大体逻辑与 AutowiredAnnotationBeanPostProcessor 实现差不多,这里不再过多讲述。

值得注意的是,@Resource 注解中有 lookup 属性,其是有关 JNDI 获取对象的方式。常规下,如果我们不在 lookup 属性进行设值,spring 会自动从 spring 容器里检索对应类型的对象。如果有设值了,则从 JDNI 的上下文去查找对应的对象。有关 JDNI 的介绍,可以看Java JNDI使用详解

3. AOP

AOP 是对对象进行封装成代理,从而对重复的代码进行有效的管理,达到切面效果。在 spring 容器,主要是在 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation 接口以及 SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference 接口进行对 Bean 创建成代理对象。其实现类主要为 AbstractAutoProxyCreator 的子类,可以先进行查看。有关 AOP 代理的介绍,后续有针对 AOP 进行详细的解读。

五 总结

在创建 Bean 过程中,spring 提供了上层接口 BeanPostProcessor,对 Bean 初始化前后提供了回调方法。例如我们可以对 Bean 的属性进行非空校验 BeanValidationPostProcessor,例如对数据源初始化后,建表等操作,目前 spring 没有实现该处理器。InstantiationAwareBeanPostProcessor 是 BeanPostProcessor 的子类,主要是针对 Bean 的实例化前后提供了回调方法,可以影响 Bean 的创建以及属性注入等操作,同时也提供了对对象的属性注入提供了回调方法,针对 @Autowired、@Value、@Resource 等注解标注的属性以及方法进行对象注入。SmartInstantiationAwareBeanPostProcessor 接口是 InstantiationAwareBeanPostProcessor 的子接口,主要是提供了创建 Bean 的构造方法,从而影响 Bean 的创建。以及解决循环引用的问题,基于这个循环引用机制,可以实现 AOP 代理对象。而 MergedBeanDefinitionPostProcessor 以及 DestructionAwareBeanPostProcessor 接口,分别是针对 BeanDefinition 的合并以及对象销毁提供了回调方法,两个接口在 spring 创建 Bean 过程,不是最重要的接口,可以先忽略。

发布于: 2021 年 04 月 17 日阅读数: 34
用户头像

邱学喆

关注

计算机原理的深度解读,源码分析。 2018.08.26 加入

在IT领域keep Learning。要知其然,也要知其所以然。原理的爱好,源码的阅读。输出我对原理以及源码解读的理解。个人的仓库:https://gitee.com/Michael_Chan

评论

发布
暂无评论
Spring Bean创建过程的Hook