【小家Spring】Spring中@Value注解有多强大?从原理层面去剖析为何它有如此大的“能耐“(上)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 【小家Spring】Spring中@Value注解有多强大?从原理层面去剖析为何它有如此大的“能耐“(上)

前言


@Value注解是Spring3.0后提出来的一个注解。注解内容本身非常之简单,但是它提供的功能却非常之强大。


首先从它的注解本身定义知道,它能使用在:


  1. 字段上
  2. set方法上
  3. 方法入参上
  4. 当作元注解


它的功能大致可归类为:

  1. 注入普通字符串
  2. 书写SpEL表达式(功能强大包括:获取系统属性、调用静态方法、计算、注入bean、调用bean的方法等等~~~)
  3. 注入Resource。如:@Value("classpath:com/demo/config.txt") 使用Resource类型接收
  4. 注入URL资源。如:@Value("http://www.baidu.com") 使用Resource类型接收


这里面不得不提的,就是它对强大的SpEL的支持,这是我们感叹@Value非常之强大的核心原因。关于SpEL的使用和原理浅析,请参见:

【小家Spring】SpEL你感兴趣的实现原理浅析spring-expression~(SpelExpressionParser、EvaluationContext、rootObject)


平时我们依赖注入你可能只会使用@Autowired等注解,但其实@Value也有注入bean的功能哦~


@Value


首先看看@Value注解本身,简单到令人发指有木有


@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
  String value(); // 没有默认值 所以是必填的
}

之前我们已经讲过,当Bean进行初始化完成之后会populateBean()对它的属性进行赋值,这个时候AutowiredAnnotationBeanPostProcessor这个后置处理器生效,从而对属性进行依赖注入赋值。


AutowiredAnnotationBeanPostProcessor它能够处理@Autowired和@Value注解~


注意:因为@Value是BeanPostProcessor来解析的,所以具有容器隔离性(本容器内的Bean使用@Value只能引用到本容器内的值哦~,因为BeanPostProcessor是具有隔离性的)

推荐:所有的@Value都写在根容器(也就是我们常说的Service容器)内,请不要放在web容器里。也就是说,请尽量不要在controller从使用@Value注解,因为业务我们都要求放在service层

三层架构:Controller、Service、Repository务必做到职责分离和松耦合~


若对Bean的初始化、实例化原理有不是非常清楚的,可抽空参见:

【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析


有了上面基础,所以我们直接从AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues()方法入手:(Spring5.1后为postProcessProperties方法,方法的语义更加清晰些了~)


public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
    implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
  // 这个方法是InstantiationAwareBeanPostProcessor的,它在给属性赋值的时候会被调用~~
  @Override
  public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // InjectionMetadata 里包含private final Collection<InjectedElement> injectedElements;表示所有需要注入处理的属性们~~~
    // 所以最终都是InjectionMetadata去处理~
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
      metadata.inject(bean, beanName, pvs);
    } ...
    return pvs;
  }
}

InjectionMetadata


用于管理注入元数据的内部类。不建议直接在应用程序中使用。AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor以及org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor这几个哥们最终的注入都是靠它~


本处直接看它的inject方法:

public class InjectionMetadata {
  public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements);
    // 遍历,一个一个处理
    if (!elementsToIterate.isEmpty()) {
      for (InjectedElement element : elementsToIterate) {
        element.inject(target, beanName, pvs);
      }
    }
  } 
}


总而言之,最终是委托给了InjectedElement 。它有两个内部类的实现:AutowiredFieldElement和AutowiredMethodElement 此处我们只关注字段注入的。


InjectedElement是InjectionMetadata的一个public内部类,并且是抽象的。AutowiredFieldElement和AutowiredMethodElement都是AutowiredAnnotationBeanPostProcessor的private内部类。

另外:CommonAnnotationBeanPostProcessor中有InjectedElement的实现类:LookupElement、ResourceElement、EjbRefElement、WebServiceRefElement等来辅助完成注入~


AutowiredFieldElement

private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
  private final boolean required;
  private volatile boolean cached = false;
  @Nullable
  private volatile Object cachedFieldValue;
  public AutowiredFieldElement(Field field, boolean required) {
    super(field, null);
    this.required = required;
  }
  @Override
  protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    // member在抽象父类:InjectedElement中定义~~~
    Field field = (Field) this.member;
    Object value;
    if (this.cached) {
      value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    }
    else {
      DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
      desc.setContainingClass(bean.getClass());
      Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
      Assert.state(beanFactory != null, "No BeanFactory available");
      // 此处一般为SimpleTypeConverter,它registerDefaultEditors=true,所以普通类型大都能能通过属性编辑器实现转换的
      TypeConverter typeConverter = beanFactory.getTypeConverter();
      try {
        // 最最最根本的原理,其实在resolveDependency这个方法里,它最终返回的就是一个具体的值,这个value是个Object~
        value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
      } catch (BeansException ex) {
        throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
      }
      synchronized (this) {
        if (!this.cached) {
          if (value != null || this.required) {
            this.cachedFieldValue = desc;
            registerDependentBeans(beanName, autowiredBeanNames);
            if (autowiredBeanNames.size() == 1) {
              String autowiredBeanName = autowiredBeanNames.iterator().next();
              if (beanFactory.containsBean(autowiredBeanName) &&
                  beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                this.cachedFieldValue = new ShortcutDependencyDescriptor(
                    desc, autowiredBeanName, field.getType());
              }
            }
          } else {
            this.cachedFieldValue = null;
          }
          this.cached = true;
        }
      }
    }
    if (value != null) {
      ReflectionUtils.makeAccessible(field);
      field.set(bean, value);
    }
  }
}


DefaultListableBeanFactory#resolveDependency


它是Spring容器整个体系里实现依赖查找的心脏~

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
    implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
  ...
  @Override
  @Nullable
  public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    if (Optional.class == descriptor.getDependencyType()) {
      // 最终它也会走到:doResolveDependency
      return createOptionalDependency(descriptor, requestingBeanName);
    }
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
        ObjectProvider.class == descriptor.getDependencyType()) {
      // 直接new一个Provider返回出去~
      return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
    // 兼容jsr330的javax.inject.Provider
    else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
      return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    }
    else {
    // ContextAnnotationAutowireCandidateResolver-> QualifierAnnotationAutowireCandidateResolver 他们能够解决  就直接返回 否则交给doResolveDependency
      Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
          descriptor, requestingBeanName);
      if (result == null) {
        result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
      }
      return result;
    }
  }
  ...
  // 咱们此处descriptor:field 'personName'
  // beanName:rootConfig
  // autowiredBeanNames:[]
  // typeConverter:SimpleTypeConverter
  @Nullable
  public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
      // 若你的despriptor是ShortcutDependencyDescriptor,这个就会直接去beanFactory.getBean(this.shortcut, this.requiredType)  提前返回  主要作用是缓存的效果~
      Object shortcut = descriptor.resolveShortcut(this);
      if (shortcut != null) {
        return shortcut;
      }
      // public final class String
      Class<?> type = descriptor.getDependencyType();
      // 备注:QualifierAnnotationAutowireCandidateResolver会处理@Qualifier和@Value
      //QualifierAnnotationAutowireCandidateResolver#getSuggestedValue()
      //先拿出@Value注解的值  如果为null再去拿Method里这个注解的值~~~ 最终返回~  所以@Value也是可以标注在方法上的
      // 注意此处:若是@Value  这里返回值肯定是String  但是若是@Autowired此处返回值就可能是对象了~
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      if (value != null) {
        // 说明这个注入是@Value  因为它是String
        if (value instanceof String) {
          // 使用StringValueResolver处理${}占位符~
          // 所以我们常用的只使用@Value("${xxx}")这样来注入值或者你就是个字面量值,到这一步就已经完事了~解析完成  
          // 若你是个el表达式  或者文件资源Resource啥的,会继续交给下面的beanExpressionResolver处理,所以它是处理复杂类型的核心~
          String strVal = resolveEmbeddedValue((String) value);
          BeanDefinition bd = (beanName != null && containsBean(beanName) ?
              getMergedBeanDefinition(beanName) : null);
          // 此处注意:处理器是BeanExpressionResolver~~~~它是处理@Value表达式的核心方法
          // 它的默认值是:StandardBeanExpressionResolver#evaluate
          // 这里面就会解析
          value = evaluateBeanDefinitionString(strVal, bd);
        }
        // 若我们没有定制,此处为SimpleTypeConverter... 值已经拿到手了,经由转换器以转换 就可以测地的返回喽~~~解析结束
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        try {
          return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
        }
        catch (UnsupportedOperationException ex) {
          // A custom TypeConverter which does not support TypeDescriptor resolution...
          return (descriptor.getField() != null ?
              converter.convertIfNecessary(value, type, descriptor.getField()) :
              converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
        }
      }
      // ====================下面就不解释了,多bean和required的解释  前面已经分析过了=======================
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
        return multipleBeans;
      }
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      if (matchingBeans.isEmpty()) {
        if (isRequired(descriptor)) {
          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
        }
        return null;
      }
      String autowiredBeanName;
      Object instanceCandidate;
      if (matchingBeans.size() > 1) {
        autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
        if (autowiredBeanName == null) {
          if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
            return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
          }
          else {
            // In case of an optional Collection/Map, silently ignore a non-unique case:
            // possibly it was meant to be an empty collection of multiple regular beans
            // (before 4.3 in particular when we didn't even look for collection beans).
            return null;
          }
        }
        instanceCandidate = matchingBeans.get(autowiredBeanName);
      }
      else {
        // We have exactly one match.
        Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
        autowiredBeanName = entry.getKey();
        instanceCandidate = entry.getValue();
      }
      if (autowiredBeanNames != null) {
        autowiredBeanNames.add(autowiredBeanName);
      }
      if (instanceCandidate instanceof Class) {
        instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
      }
      Object result = instanceCandidate;
      if (result instanceof NullBean) {
        if (isRequired(descriptor)) {
          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
        }
        result = null;
      }
      if (!ClassUtils.isAssignableValue(type, result)) {
        throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
      }
      return result;
    }
    finally {
      ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
  }
}

、QualifierAnnotationAutowireCandidateResolver实现了AutowireCandidateResolver,对要自动绑定的field或者参数和bean definition根据@qualifier注解进行匹配,当然也支持javax.inject.Qualifier。同时也支持通过@value注解来绑定表达式的值。


从上面分析知道,当把@Value的占位符替换完成后,最终都会交给beanExpressionResolver由它来统一处理:包括根据beanName获取bean、SpEL计算等等~~~

相关文章
|
26天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
169 73
|
21天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
50 21
|
26天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
26天前
|
Java Spring
【Spring配置】idea编码格式导致注解汉字无法保存
问题一:对于同一个项目,我们在使用idea的过程中,使用汉字注解完后,再打开该项目,汉字变成乱码问题二:本来a项目中,汉字注解调试好了,没有乱码了,但是创建出来的新的项目,写的注解又成乱码了。
|
1月前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
106 14
|
2月前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
56 4
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
188 2
|
2月前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
70 2
|
2月前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
52 2