BeanExpressionResolver
策略接口,用于通过将值作为表达式进行评估来解析值(如果适用)。它持有Bean工厂~
// @since 3.0 public interface BeanExpressionResolver { // value此时还是复杂类型,比如本例的#{person.name} // BeanExpressionContext:持有beanFactory和scope的引用而已~ @Nullable Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException; }
它的唯一实现类StandardBeanExpressionResolver(当然我们是可以自己实现的,比如后面我们自定义@Value功能,通过继承StandardBeanExpressionResolver来扩展实现~毕竟Spring很暖心的给我们开了口)
StandardBeanExpressionResolver
语言解析器的标准实现,支持解析SpEL语言。
public class StandardBeanExpressionResolver implements BeanExpressionResolver { // 因为SpEL是支持自定义前缀、后缀的 此处保持了和SpEL默认值的统一 // 它的属性值事public的 so你可以自定义~ /** Default expression prefix: "#{". */ public static final String DEFAULT_EXPRESSION_PREFIX = "#{"; /** Default expression suffix: "}". */ public static final String DEFAULT_EXPRESSION_SUFFIX = "}"; private String expressionPrefix = DEFAULT_EXPRESSION_PREFIX; private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX; private ExpressionParser expressionParser; // 它的最终值是SpelExpressionParser // 每个表达式都对应一个Expression,这样可以不用重复解析了~~~ private final Map<String, Expression> expressionCache = new ConcurrentHashMap<>(256); // 每个BeanExpressionContex都对应着一个取值上下文~~~ private final Map<BeanExpressionContext, StandardEvaluationContext> evaluationCache = new ConcurrentHashMap<>(8); // 匿名内部类 解析上下文。 和TemplateParserContext的实现一样。个人觉得直接使用它更优雅 // 和ParserContext.TEMPLATE_EXPRESSION 这个常量也一毛一样 private final ParserContext beanExpressionParserContext = new ParserContext() { @Override public boolean isTemplate() { return true; } @Override public String getExpressionPrefix() { return expressionPrefix; } @Override public String getExpressionSuffix() { return expressionSuffix; } }; // 空构造函数:默认就是使用的SpelExpressionParser 下面你也可以自己set你自己的实现~ public StandardBeanExpressionResolver() { this.expressionParser = new SpelExpressionParser(); } public void setExpressionParser(ExpressionParser expressionParser) { Assert.notNull(expressionParser, "ExpressionParser must not be null"); this.expressionParser = expressionParser; } // 解析代码相对来说还是比较简单的,毕竟复杂的解析逻辑都是SpEL里边~ 这里只是使用一下而已~ @Override @Nullable public Object evaluate(@Nullable String value, BeanExpressionContext evalContext) throws BeansException { if (!StringUtils.hasLength(value)) { return value; } try { Expression expr = this.expressionCache.get(value); if (expr == null) { // 注意:此处isTemplte=true expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext); this.expressionCache.put(value, expr); } // 构建getValue计算时的执行上下文~~~ // 做种解析BeanName的ast为;org.springframework.expression.spel.ast.PropertyOrFieldReference StandardEvaluationContext sec = this.evaluationCache.get(evalContext); if (sec == null) { // 此处指定的rootObject为:evalContext --> BeanExpressionContext sec = new StandardEvaluationContext(evalContext); // 此处新增了4个,加上一个默认的 所以一共就有5个属性访问器了 // 这样我们的SpEL就能访问BeanFactory、Map、Enviroment等组件了~ // BeanExpressionContextAccessor表示调用bean的方法~~~~(比如我们此处就是使用的它) 最终执行者为;BeanExpressionContext 它持有BeanFactory的引用嘛~ // 如果是单村的Bean注入,最终使用的也是BeanExpressionContextAccessor 目前没有找到BeanFactoryAccessor的用于之地~~~ // addPropertyAccessor只是:addBeforeDefault 所以只是把default的放在了最后,我们手动add的还是保持着顺序的~ // 注意:这些属性访问器是有先后顺序的,具体看下面~~~ sec.addPropertyAccessor(new BeanExpressionContextAccessor()); sec.addPropertyAccessor(new BeanFactoryAccessor()); sec.addPropertyAccessor(new MapAccessor()); sec.addPropertyAccessor(new EnvironmentAccessor()); // setBeanResolver不是接口方法,仅仅辅助StandardEvaluationContext 去获取Bean sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory())); sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader())); // 若conversionService不为null,就使用工厂的。否则就使用SpEL里默认的DefaultConverterService那个 // 最后包装成TypeConverter给set进去~~~ ConversionService conversionService = evalContext.getBeanFactory().getConversionService(); if (conversionService != null) { sec.setTypeConverter(new StandardTypeConverter(conversionService)); } // 这个很有意思,是一个protected的空方法,因此我们发现若我们自己要自定义BeanExpressionResolver,完全可以继承自StandardBeanExpressionResolver // 因为我们绝大多数情况下,只需要提供更多的计算环境即可~~~~~ customizeEvaluationContext(sec); this.evaluationCache.put(evalContext, sec); } return expr.getValue(sec); } catch (Throwable ex) { throw new BeanExpressionException("Expression parsing failed", ex); } } //Spring留给我们扩展的SPI protected void customizeEvaluationContext(StandardEvaluationContext evalContext) { } }
如上,整个@Value的解析过程至此就全部完成了。可能有小伙伴会问:怎么不见Resource这种注入呢?其实,从上面不难看出,这个是ConversionService去做的事,它能够把一个字符串转换成Resource对象,仅此而已
总得来说@Value它自己做的事本身还是非常单一的:依赖注入,只是它把众多功能都很好的像插件一样插拔进来了,从而对用户很友好的显示了显它的神通广大~
需要注意的是,在整个依赖的解析过程中,有两个非常重要的接口:BeanExpressionResolver和AutowireCandidateResolver都扮演着重要角色,有兴趣的可以深入继续了解~
SpEL中PropertyAccessor的匹配规则和执行顺序
本来这个内容应该放在SpEL章节更为合适,但是就着这个Spring的环境,就放在此处了。
如上StandardEvaluationContext中默认就会给放在上4+1个PropertyAccessor,他们的匹配规则和执行顺序势必会影响到最终的取值,因此这部分就看看他们的决策原理。
备注:这部分的理解对平时的使用几乎没有关系,但对你自定义扩展功能有较大的影响~
默认注册后,他们的顺序如下:
它选择的代码在此处(SpEL章节说到了,目前只有PropertyOrFieldReference
这个AST才会涉及到PropertyAccessor
的决策):
// 它是一个AST~ public class PropertyOrFieldReference extends SpelNodeImpl { ... // 按照本例而言,contextObject就为BeanExpressionContext private List<PropertyAccessor> getPropertyAccessorsToTry(@Nullable Object contextObject, List<PropertyAccessor> propertyAccessors) { Class<?> targetType = (contextObject != null ? contextObject.getClass() : null); List<PropertyAccessor> specificAccessors = new ArrayList<>(); List<PropertyAccessor> generalAccessors = new ArrayList<>(); // 此处propertyAccessors默认会有5个值,如上截图 for (PropertyAccessor resolver : propertyAccessors) { Class<?>[] targets = resolver.getSpecificTargetClasses(); if (targets == null) { // generic resolver that says it can be used for any type // 如果为null没指定类型,那就相当于这个处理器可议处理任意type generalAccessors.add(resolver); } else if (targetType != null) { for (Class<?> clazz : targets) { //你的处理器指定的targetType和我的完全一样,我才选中这个处理器,加入到specificAccessors // 找到了就立马break~~~ 所以我说 他们顺序是很重要的 // specificAccessors里的值要么为[] 要么只有一个 if (clazz == targetType) { specificAccessors.add(resolver); break; } // targetType若是clazz的子类,加入到generalAccessors 作为通用的处理器 else if (clazz.isAssignableFrom(targetType)) { generalAccessors.add(resolver); } } } } // 这里有些神操作~~~resolvers 作为最终返回 // 1、 先从generalAccessors里面移除掉了所有已匹配上的~~~比如此处generalAccessors的size为0~ // 2、移除好后把它在添加进resolvers里面去~~~~ List<PropertyAccessor> resolvers = new ArrayList<>(specificAccessors); generalAccessors.removeAll(specificAccessors); resolvers.addAll(generalAccessors); return resolvers; } ... }
如此,我们每一种待处理的类型,一般最终得到的都是唯一一个resolver
,从而完成对该引用的取值。