Spring IOC 容器初始化 -- 表达式解析

「这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

1. 前言

本文主要讲解,Spring IOC 容器初始化的时候,bean 中字段的表达式是如何解析的。

在正式看源码之前,我们需要了解一下 BeanExpressionResolver : bean的表达式解析器,它是一个接口,里面只有一个 evaluate 方法,如果我们想要自定义表达式,并希望sprig 能解析它,就可以实现这个接口,在该方法中,重写表达式解析逻辑,然后把这个表达式解析器 注册到工厂中即可。

图片.png

这里我主要还是以spring 默认的解析器(SPEL表达式解析器)来讲解。

2.表达式解析器在哪里注册到容器的?

容器初始化时的 refresh() --》 prepareBeanFactory(beanFactory)

setbeanExpresssion.jpg

2.1 StandardBeanExpressionResolver

这里源码太长我就不全贴了我们先看一下,它的结构图

图片.png

可以看出这里 StandardBeanExpressionResolver 实现了 BeanExpressionResolver。

图片.png 这是它的构造方法,这里实际上就是先造出 一个 SpelParserConfiguration(SPEL解析器配置类)

图片.png 图片.png

通过造出来 SpelParserConfiguration 实例,再去构建一个 SpelExpressionParser

图片.png

把这个实例赋给 StandardBeanExpressionResolver的 expressionParser 即可。

3.我们该如何设置自定义的 表达式解析器

通过前面 我们查看源码,发现spring 容器 添加的默认SPEL 表达式解析器就是 StandardBeanExpressionResolver,那我们自定义应该在那个地方添加呢?

  1. 我们可以自定义一个容器,重写 postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 。postProcessBeanFactory()的详解 我在前面讲过了,感兴趣的可以看看。

如:

图片.png 图片.png

  1. 我们可以 自定义一个 BeanFactoryPostProcessor,实现它的 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; BeanFactory 的 PostProcessor的具体调用过程,我在前面也讲过了,大家感兴趣的可以看看

图片.png

4.具体是在哪里进行表达式解析的?

refresh() -->
finishBeanFactoryInitialization(beanFactory) --> beanFactory.preInstantiateSingletons() --> getBean(beanName) -->
doGetBean(final String name, @Nullable final Class requiredType,@Nullable final Object[] args, boolean typeCheckOnly) -->
createBean(beanName, mbd, args) -->
resolveBeanClass(mbd, beanName) --> doResolveBeanClass(mbd, typesToMatch) --> evaluateBeanDefinitionString(className, mbd)

可能看起来有点绕,但解析表达式的主要代码就在这里,这里的话,以默认解析器为例,源码如下:

图片.png

  1. 这里先判断当前 beanFactory 的 BeanExpressionResolver 不为null的时候,进行属性解析。
  2. 判断当Beandefinition 不为null时,先取出作用域名,再通过名字取出作用域。
    • this.scopes: 保存作用域标识符字符串映射到相应的作用域

图片.png 图片.png 3.构建一个 BeanExpressionContext 的实例 图片.png 4. 调用解析器的 evaluate() 方法。

4.1 evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException

图片.png

  1. 当value 是空串时,直接返回

  2. 从 expressionCache 取出对应的表达式

    • this.expressionCache: 缓存name -->Expression

    图片.png

  3. 当表达式为null时,调用 parseExpression 生成一个Expression 对象 并给它放到缓存中去

  4. 再次从缓存中取 表达式,赋给变量 sec

    • 这里先说一下,EvaluationContext:表示上下文环境(表达式执行的上下文环境),设置根对象、自定义变量、自定义函数、类型转换器等,默认实现是:org.springframework.expression.spel.support.StandardEvaluationContext

    • 这里相当于 实例了一个默认的上下文环境。

  5. PropertyAccessor:属性访问控制器,主要控制bean属性的存取,这里也是完成数据绑定的关键部件。源码中也是给他添加了几个 PropertyAccessor 的实现类。

  6. ConversionService : 主要作用是类型转换,这里先去它自己的类型转换器,若没取到则给它添加一个默认的。

  7. customizeEvaluationContext(sec) : 这里默认是一个空实现,主要交给子类实现,用来做一些自定义的配置

  8. 把这个表达式,及对应的上下文环境,再次添加到缓存中去。

  9. 返回 expr.getValue(sec) : 这里主要就是对表达式进行处理,返回结果值。大家有兴趣可以看一看。

图片.png

猜你喜欢

转载自juejin.im/post/7031048271036432391