我正在参与「启航计划」

搞不懂,为啥redis操作对象的注入方式跟平常的不一样?

不知道咱们平时有没有见过或许写过这样的代码。

这样写是错误的吧,idea编译器都辨认报错了呀?

分明两个不同类型的类,也能经过spring注入?

为啥这些redis的操作类都经过redisTemplate注入?

既然我这样写了,自然是没有问题的。

要不,带着这些问题跟我持续往下看?

提出猜测

咱们都知道ValueOperations操作目标都是能够直接经过RedisTemplate直接取得:

ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

那么咱们是不是能够有以下猜测(以注入ValueOperations为例)。

  • @Resource(name = “redisTemplate”)

    • ①首要从spring容器中获取到现已初始化好的RedisTemplate目标。

    • ②再经过redisTemplate.opsForValue()获取到ValueOperations目标。

  • private ValueOperations<String, String> stringValueOperations;

    • ③终究获取的ValueOperations目标赋值给stringValueOperations变量。

验证猜测

获取bean:doGetBean

既然是spring的注入问题,咱们就需求翻开获取spring bean逻辑的代码doGetBean

protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException

说明一下参数的含义

  • name: 需求注入的目标名
  • requiredType: 希望回来目标的class
  • args: 显式参数创建bean实例时要运用的参数
  • typeCheckOnly: 是否为类型检查获取实例

而咱们需求重点重视的就两个参数:name和requiredType。

依据咱们的业务场景,大概来理解下这两个参数

name是不是就等于”redisTemplate”

而requiredType是不是便是ValueOperations.class

这时,你有没有恍然大悟的感觉,原来spring本来便是支撑注入的bean类型和回来的类型不一样的啊。

那spring是怎么完成的呢,咱们接着往下看(只截取要害代码)

// 假如从spring容器获取的目标和希望回来的目标类型不一样,履行此逻辑
if (requiredType != null && !requiredType.isInstance(bean)) {
   try {
      T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
      if (convertedBean == null) {
         throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
      return convertedBean;
   }
   catch (TypeMismatchException ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Failed to convert bean '" + name + "' to required type '" +
               ClassUtils.getQualifiedName(requiredType) + "'", ex);
      }
      throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
   }
}

重点便是T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType) 这行代码了。 他完成了bean的转化逻辑

转化bean:doConvertValue

深化这行代码,就能找到真实履行转化逻辑的代码(TypeConverterDelegate.class)。篇幅有限,只截取要害代码。

@Nullable
private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
      @Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {
   Object convertedValue = newValue;
   if (editor != null && !(convertedValue instanceof String)) {
      try {
         editor.setValue(convertedValue);
         Object newConvertedValue = editor.getValue();
         if (newConvertedValue != convertedValue) {
            convertedValue = newConvertedValue;
            editor = null;
         }
      }
      catch (Exception ex) {
         if (logger.isDebugEnabled()) {
            logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
         }
      }
   }
   Object returnValue = convertedValue;
   return returnValue;
}

重点需求重视两行代码

editor.setValue(convertedValue);
Object newConvertedValue = editor.getValue();

能够看到spring将两个目标的转化交给了从spring容器获取的beanPropertyEditor目标去处理:

  • spring先是给PropertyEditor目标设置了从spring容器中获取到的目标

  • 然后直接从PropertyEditor目标获取目标

  • 终究就回来了这个目标

那么PropertyEditor这个目标是啥,他又是怎么组合从spring容器获取的bean从而获取到想要的目标的呢?

PropertyEditor

简介

官方注释对PropertyEditor进行了说明

PropertyEditor类为希望允许用户修改给定类型的特点值的GUI供给支撑。
PropertyEditor支撑显现和更新特点值的各种不同办法。大多数PropertyEditors只需求支
持此API中可用的不同选项的子集。
Simple PropertyEditors或许只支撑getAsText和setAsText办法,不需求支撑
paintValue或getCustomEditor。更杂乱的类型或许无法支撑getAsText和setAsText,
但将支撑paintValue和getCustomEditor。
每个propertyEditor都有必要支撑三种简略显现款式中的一种或多种。
因而,它能够(1)支撑isPaintable,或许(2)从getTags()回来一个非null String[],
并从getAsText()中回来非null值,或许(3)只从getAs文本()回来非null String。
当参数目标的类型为propertyEditor时,每个特点修改器都有必要支撑对setValue的调用。
此外,每个特点修改器有必要支撑自定义修改器或支撑setAsText。
每个PropertyEditor都应该有一个空结构函数。

说白了,JDK供给了这个接口,便是为了将外部设置的值(RedisTemplate)转化为内部的特点值(value = ValueOperations)。

跟ValueOperations的联系

先抛开PropertyEditor这个目标,回到咱们要转化的类型ValueOperations上,翻开ValueOperations类地点的位置。

搞不懂,为啥redis操作对象的注入方式跟平常的不一样?

有没有发现地点目录下,每一种Operations都有对应的Editor,咱们选择ValueOperationsEditor看看他的UML类图

搞不懂,为啥redis操作对象的注入方式跟平常的不一样?

公然,便是咱们要找的PropertyEditor!

ValueOperationsEditor源码

接着咱们来看下ValueOperationsEditor的源码,看他都做了些啥。

class ValueOperationsEditor extends PropertyEditorSupport {
    ValueOperationsEditor() {
    }
    public void setValue(Object value) {
        if (value instanceof RedisOperations) {
            super.setValue(((RedisOperations)value).opsForValue());
        } else {
            throw new IllegalArgumentException("Editor supports only conversion of type " + RedisOperations.class);
        }
    }
}

公然,跟咱们的猜测一样,终究便是经过opsForValue()办法从RedisTemplate获取到真实的操作目标,并设置到Editor目标的value特点中,然后调用getValue回来咱们希望获取的目标ValueOperations。

总结

现实证明了咱们的猜测大致的思路是没问题的,可是需求补充点细节。

那终究,咱们就将之前的猜测补充成终究的定论吧。

  • 首要获取到spring容器中的RedisTemplate目标
  • 然后获取到ValueOperations对应的修改类ValueOperationsEditor
  • 将RedisTemplate目标经过set办法给ValueOperationsEditor的特点值value赋值
  • 终究经过get办法得到特点值,即ValueOperations目标

文中如有不足之处,欢迎指正!一同交流,一同学习,一同生长 ^v^