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解析器配置类)

扫描二维码关注公众号,回复: 13171281 查看本文章

图片.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