我正在参与「启航计划」
不知道咱们平时有没有见过或许写过这样的代码。
这样写是错误的吧,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容器获取的bean和PropertyEditor目标去处理:
-
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类地点的位置。
有没有发现地点目录下,每一种Operations都有对应的Editor,咱们选择ValueOperationsEditor看看他的UML类图
公然,便是咱们要找的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^