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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 【小家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计算等等~~~

相关文章
|
2月前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
125 1
|
2月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
2月前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
53 0
|
8天前
|
Java Spring 容器
Spring使用异步注解@Async正确姿势
Spring使用异步注解@Async正确姿势,异步任务,spring boot
|
7天前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
8天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
21天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
2月前
|
Java 数据安全/隐私保护 Spring
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
|
2月前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
2月前
|
监控 安全 Java
【开发者必备】Spring Boot中自定义注解与处理器的神奇魔力:一键解锁代码新高度!
【8月更文挑战第29天】本文介绍如何在Spring Boot中利用自定义注解与处理器增强应用功能。通过定义如`@CustomProcessor`注解并结合`BeanPostProcessor`实现特定逻辑处理,如业务逻辑封装、配置管理及元数据分析等,从而提升代码整洁度与可维护性。文章详细展示了从注解定义、处理器编写到实际应用的具体步骤,并提供了实战案例,帮助开发者更好地理解和运用这一强大特性,以实现代码的高效组织与优化。
56 0
下一篇
无影云桌面