优化@Value注解

master
xuchengsheng 2023-10-16 17:32:52 +08:00
parent 28f6dcfd93
commit a66a8f2d29
1 changed files with 390 additions and 16 deletions

View File

@ -69,8 +69,8 @@ public @interface Value {
1. **提供属性注入**
+ 允许从不同的配置源(如属性文件、系统属性等)直接向 Spring 管理的 beans 中注入值。
2. **支持表达式**
- **SpEL (Spring Expression Language) 表达式**:例如,`#{systemProperties.myProp}` 可以从系统属性中获取名为 `myProp` 的值。
- **属性占位符**:例如,`${my.app.myProp}` 可以从预定义的配置源,如 `application.properties``application.yml` 文件,获取名为 `my.app.myProp` 的属性值。
- SpEL (Spring Expression Language) 表达式:例如,`#{systemProperties.myProp}` 可以从系统属性中获取名为 `myProp` 的值。
- 属性占位符:例如,`${my.app.myProp}` 可以从预定义的配置源,如 `application.properties``application.yml` 文件,获取名为 `my.app.myProp` 的属性值。
3. **动态值解析**
+ 与只能在启动时设置静态值相比,`@Value` 注解可以解析动态表达式,从而为字段或构造函数参数提供动态值。
4. **用于字段、方法参数、构造函数参数和注解**
@ -338,7 +338,6 @@ private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
```java
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
// ... [代码部分省略以简化]
}
@ -399,11 +398,11 @@ public void inject(Object target, @Nullable String beanName, @Nullable PropertyV
```java
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 获取代表带有@Autowired注解的字段的Field对象。
// 步骤1. 获取代表带有@Autowired注解的字段的Field对象。
Field field = (Field) this.member;
Object value;
// 如果字段的值已经被缓存(即先前已解析过),则尝试从缓存中获取。
// 步骤2. 如果字段的值已经被缓存(即先前已解析过),则尝试从缓存中获取。
if (this.cached) {
try {
// 从缓存中获取已解析的字段值。
@ -415,21 +414,21 @@ protected void inject(Object bean, @Nullable String beanName, @Nullable Property
}
}
else {
// 如果字段值未被缓存,直接解析。
// 步骤3. 如果字段值未被缓存,直接解析。
value = resolveFieldValue(field, bean, beanName);
}
// 如果解析到的值不为null...
// 步骤4. 如果解析到的值不为null...
if (value != null) {
// 使字段可访问这是必要的特别是当字段是private时。
// 步骤4.1. 使字段可访问这是必要的特别是当字段是private时。
ReflectionUtils.makeAccessible(field);
// 实际将解析的值注入到目标bean的字段中。
// 步骤4.2. 实际将解析的值注入到目标bean的字段中。
field.set(bean, value);
}
}
```
在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue`方法中,通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值。
首先来到`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject`方法中的步骤3。在`org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue`方法中,通过`beanFactory.resolveDependency`方法从Spring的bean工厂中解析字段的值。
```java
@Nullable
@ -448,6 +447,379 @@ private Object resolveFieldValue(Field field, Object bean, @Nullable String bean
}
```
在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中,首先尝试获取一个延迟解析代理。如果无法获得,它会进一步尝试解析依赖。`doResolveDependency` 是实际进行解析工作的方法。
```java
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// ... [代码部分省略以简化]
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
```
在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中,首先从`DependencyDescriptor`获取注解值然后处理其中的字符串属性占位符和SpEL表达式。最后确保值根据目标字段或参数类型进行正确的类型转换并将其注入相应的位置。
```java
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// ... [其他代码部分省略以简化]
try {
// 尝试快速解析依赖
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
// 获取依赖的类型
Class<?> type = descriptor.getDependencyType();
// 步骤1. 获取依赖的建议值,例如@Value注解的值
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
// 如果建议的值是字符串类型
if (value instanceof String) {
// 步骤2. 解析嵌入的值,如处理属性占位符
String strVal = resolveEmbeddedValue((String) value);
// 获取与bean名称相关的BeanDefinition
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
// 步骤3. 对Bean定义字符串进行评估如处理SpEL表达式
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
// 步骤4. 获取类型转换器并进行必要的类型转换
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
// ... [其他代码部分省略以简化]
}
// ... [其他代码部分省略以简化]
}
// ... [其他代码部分省略以简化]
}
```
首先来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤1。在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#getSuggestedValue`方法中,主要是用于解析与`DependencyDescriptor`相关的注解值,特别是`@Value`注解。如果字段或方法参数上有`@Value`注解,它会从注解中提取相应的值或表达式。
```java
@Override
@Nullable
public Object getSuggestedValue(DependencyDescriptor descriptor) {
// 从描述符的注解中查找@Value注解提供的值
Object value = findValue(descriptor.getAnnotations());
// 如果在描述符的注解中没有找到,检查是否存在与此描述符关联的方法参数
if (value == null) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
// 如果存在方法参数,再从方法参数的注解中查找@Value提供的值
value = findValue(methodParam.getMethodAnnotations());
}
}
// 返回找到的值如果没有找到则返回null
return value;
}
```
在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#findValue`方法中,目标是在提供的注解集合中找到并返回`@Value`注解的值。如果没有找到它会返回null。
```java
protected Object findValue(Annotation[] annotationsToSearch) {
if (annotationsToSearch.length > 0) { // qualifier annotations have to be local
AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
if (attr != null) {
return extractValue(attr);
}
}
return null;
}
```
在`org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#extractValue`方法中,目的是从`AnnotationAttributes`对象中直接提取`@Value`注解的值。如果没有提供值,它会抛出异常。
```java
protected Object extractValue(AnnotationAttributes attr) {
Object value = attr.get(AnnotationUtils.VALUE);
if (value == null) {
throw new IllegalStateException("Value annotation must have a value attribute");
}
return value;
}
```
当我们使用 `@Value("${app.description:我是默认值}")` 在你的字段上时Spring 会在运行时尝试解析这个属性占位符。当 Spring 容器处理这个字段的注入时,它会使用 `QualifierAnnotationAutowireCandidateResolver`(或其他相关的后处理器)来获取并解析这个属性值。在我们的最佳实践下,`extractValue` 方法就是从注解属性中提取该属性占位符的逻辑,即返回值为 `"${app.description:我是默认值}"`。这个值随后会被 Spring 的属性解析器进一步处理,解析真实的值或使用默认值,并最终注入到 `appDescription` 字段中。
```java
${app.description:我是默认值}
```
然后我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤2。在`org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue`方法中,用于解析给定字符串中的内嵌值。它遍历所有注册的`StringValueResolver`解析器,对给定的字符串值进行连续解析,以处理可能存在的多重内嵌值或引用。例如,如果字符串中有一个`${property}`形式的属性,它可以通过注册的解析器进行处理和解析为实际的属性值。
```java
@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
// 初始检查如果提供的值为null则直接返回null
if (value == null) {
return null;
}
String result = value;
// 遍历所有的内嵌值解析器
for (StringValueResolver resolver : this.embeddedValueResolvers) {
// 使用当前解析器解析result中的值
result = resolver.resolveStringValue(result);
// 如果解析后的值为null则直接返回null
if (result == null) {
return null;
}
}
// 返回所有解析器处理后的最终值
return result;
}
```
然后我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤3。在`org.springframework.beans.factory.support.AbstractBeanFactory#evaluateBeanDefinitionString`方法中用于评估给定的字符串值特别是处理可能包含Spring表达式语言 (SpEL) 表达式的字符串。首先,它检查是否有一个`beanExpressionResolver`可用来解析SpEL。如果有它可能会获取bean定义的作用域如果提供了bean定义然后使用`beanExpressionResolver`对字符串值进行评估,并考虑到相关的作用域上下文。
```java
@Nullable
protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
// 如果没有设置bean表达式解析器直接返回原始值
if (this.beanExpressionResolver == null) {
return value;
}
Scope scope = null;
// 如果提供了bean定义
if (beanDefinition != null) {
// 获取bean的作用域
String scopeName = beanDefinition.getScope();
// 如果作用域名称不为空,则尝试从已注册的作用域中获取对应的作用域
if (scopeName != null) {
scope = getRegisteredScope(scopeName);
}
}
// 使用bean表达式解析器解析提供的值并返回结果
// 这可以处理例如使用Spring EL的情况
return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
}
```
在`org.springframework.context.expression.StandardBeanExpressionResolver#evaluate`方法中主要目的是解析并评估给定的值可能是一个Spring EL表达式。为了提高性能它使用缓存来存储先前解析的表达式和评估上下文。此方法首先从缓存中检索或解析表达式然后准备一个评估上下文并使用它评估表达式。这个评估上下文被配置为能够访问与Spring容器相关的各种内容如beans、环境属性等。
```java
@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) {
expr = this.expressionParser.parseExpression(value, this.beanExpressionParserContext);
this.expressionCache.put(value, expr);
}
// 尝试从缓存中获取评估上下文
StandardEvaluationContext sec = this.evaluationCache.get(evalContext);
// 如果缓存中没有评估上下文,则创建一个新的,并进行一些初始化配置
if (sec == null) {
sec = new StandardEvaluationContext(evalContext);
// 添加各种属性访问器以支持对特定类型的属性的访问
sec.addPropertyAccessor(new BeanExpressionContextAccessor());
sec.addPropertyAccessor(new BeanFactoryAccessor());
sec.addPropertyAccessor(new MapAccessor());
sec.addPropertyAccessor(new EnvironmentAccessor());
// 设置bean解析器和类型定位器
sec.setBeanResolver(new BeanFactoryResolver(evalContext.getBeanFactory()));
sec.setTypeLocator(new StandardTypeLocator(evalContext.getBeanFactory().getBeanClassLoader()));
// 如果有可用的转换服务,则设置类型转换器
ConversionService conversionService = evalContext.getBeanFactory().getConversionService();
if (conversionService != null) {
sec.setTypeConverter(new StandardTypeConverter(conversionService));
}
// 自定义评估上下文,允许子类提供额外的配置
customizeEvaluationContext(sec);
// 将创建的评估上下文放入缓存
this.evaluationCache.put(evalContext, sec);
}
// 使用已准备好的评估上下文评估表达式并返回结果
return expr.getValue(sec);
}
catch (Throwable ex) {
// 如果在解析或评估过程中出现任何异常抛出BeanExpressionException
throw new BeanExpressionException("Expression parsing failed", ex);
}
}
```
然后我们来到在`org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency`方法中的步骤4。在`org.springframework.beans.TypeConverterSupport#convertIfNecessary(value,requiredType,typeDescriptor)`方法中,又重新委托给`typeConverterDelegate`进行实际的转换工作
```java
@Nullable
@Override
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {
Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate");
try {
// 委托给typeConverterDelegate进行实际的转换工作
return this.typeConverterDelegate.convertIfNecessary(null, null, value, requiredType, typeDescriptor);
}
catch (ConverterNotFoundException | IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (ConversionException | IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
```
在`org.springframework.beans.TypeConverterDelegate#convertIfNecessary(propertyName,oldValue,newValue,requiredType,typeDescriptor)`方法中,负责将一个值转换为必需的类型。首先,它会尝试查找对应的自定义编辑器。如果没有找到编辑器但设置了自定义的转换服务,它会尝试使用此服务进行转换。如果上述两步都失败,该方法还会尝试执行一些标准的转换规则,例如从字符串到枚举或从数字到其他数字类型的转换。如果所有尝试都失败,该方法会抛出相应的异常,指出不能执行所需的转换。
```java
public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// 查找此类型的自定义编辑器
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
// 用于捕获ConversionService尝试失败的异常
ConversionFailedException conversionAttemptEx = null;
// 没有自定义编辑器但是否指定了自定义ConversionService
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
conversionAttemptEx = ex; // 记录转换尝试失败
}
}
}
Object convertedValue = newValue;
// 如果值不是所需类型,进行转换
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
convertedValue instanceof String) {
TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
if (elementTypeDesc != null) {
Class<?> elementType = elementTypeDesc.getType();
if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType); // 如果没有自定义编辑器,找默认的
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor); // 进行转换
}
// 对于特定情况尝试标准类型转换,如字符串到枚举、数字转换等
boolean standardConversion = false;
if (requiredType != null) {
if (convertedValue != null) {
// 若目标类型为Object则直接返回转换值
if (Object.class == requiredType) {
return (T) convertedValue;
}
// 若目标类型为数组,进行数组转换
else if (requiredType.isArray()) {
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
// 如果是Collection或Map,则尝试转换集合或映射的内容
else if (convertedValue instanceof Collection) {
convertedValue = convertToTypedCollection((Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
else if (convertedValue instanceof Map) {
convertedValue = convertToTypedMap((Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && trimmedValue.isEmpty()) {
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass((Number) convertedValue, (Class<Number>) requiredType);
standardConversion = true;
}
}
else if (requiredType == Optional.class) {
convertedValue = Optional.empty();
}
// 如果经过上述所有转换后,值仍不匹配目标类型,则抛出异常
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (conversionAttemptEx != null) {
throw conversionAttemptEx;
}
else if (conversionService != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
}
throw new IllegalStateException("转换失败"); // 实际异常消息会更详细,这里简化了
}
}
if (conversionAttemptEx != null) {
throw conversionAttemptEx;
}
return (T) convertedValue;
}
```
### 八、注意事项
1. **SpEL表达式**
@ -495,10 +867,12 @@ private Object resolveFieldValue(Field field, Object bean, @Nullable String bean
1. **核心后处理器**
+ `AutowiredAnnotationBeanPostProcessor`是处理`@Value`等注解的主要后处理器。它实现了两个关键的接口,`MergedBeanDefinitionPostProcessor`和`InstantiationAwareBeanPostProcessor`这两个接口允许在bean的生命周期中的关键阶段进行干预为属性注入提供了机制。
2. **收集阶段**
+ 在`postProcessMergedBeanDefinition`方法中后处理器确保给定的bean定义与预期的自动装配元数据一致。主要任务是为给定的bean名称和类型查找相关的`InjectionMetadata`这可能包含了该bean的字段和方法的注入信息。
- 在`postProcessMergedBeanDefinition`方法中,`AutowiredAnnotationBeanPostProcessor`确保bean的定义与预期的自动装配元数据匹配。
- `findAutowiringMetadata`方法确保为给定的bean名称和类获取相关的`InjectionMetadata`,并利用缓存机制优化性能。
- `buildAutowiringMetadata`方法检查类及其所有父类,确定带有`@Autowired`、`@Value`等注解的字段和方法,并为这些元素创建一个统一的`InjectionMetadata`对象。
3. **注入阶段**
+ 在`postProcessProperties`方法中后处理器处理bean属性的注入特别是通过`@Value`进行的注入。具体来说它会获取与bean名称和类相关的`InjectionMetadata`,然后使用这些元数据来注入属性。
4. **字段注入**
+ 在`AutowiredFieldElement#inject`方法中首先会检查字段的值是否已经被缓存。如果已缓存则从缓存中获取否则重新解析。然后将解析的值注入到目标bean的字段中
5. **值解析**
+ 在`AutowiredFieldElement#resolveFieldValue`方法中后处理器从Spring的bean工厂中解析字段的值。它主要通过`beanFactory.resolveDependency`方法来完成这一工作
+ `postProcessProperties`方法用于处理bean的属性的后处理特别是注入由`@Value`等注解标记的属性。
+ `InjectionMetadata#inject`方法用于将所有需要注入的元素(例如带有`@Value`的字段注入到目标bean中。
+ `AutowiredFieldElement#inject`方法处理具体的字段注入,包括解析`@Value`注解中的值
+ `DefaultListableBeanFactory#resolveDependency`方法从Spring的bean工厂中解析字段的值。
+ `DefaultListableBeanFactory#doResolveDependency`方法是实际解析工作的主要场所,涉及到处理`@Value`注解中的字符串属性占位符和SpEL表达式并确保值经过正确的类型转换