当前位置:   article > 正文

【springboot源码】深度解析@Value赋值时机及底层原理_@value原理

@value原理

1.@Value使用

@Value主要是让我们程序动态的将外部值注入到bean中的,使用方式主要如下两种:

1.1@Value("${}"):可以获取对应属性文件中定义的属性值。

1.2@Value("#{}"):表示 SpEl 表达式通常用来获取 bean 的属性,或者调用 bean 的某个方法。

下面不做代码演示,只是看看它底层实现的时机

2.获取时机

2.1简单说下spring bean的生命周期

为啥要说下bean的生命周期,主要是@Value注入到我们的bean属性上,那么必然牵扯spring bean的生命周期,看到网上好多帖子直接讲解对应的实现方法,我觉得看完之后,大家会云里来雾里去

点击springboot的启动类,一直往下找到这个方法,点进去找到refresh方法

spring bean的生命周期主要就在refresh方法中

红线的方法其实就是初始化了所有的singleton beans

点击红线的方法,继续往下看:

先找到getBean=>doGetBean=>createBean=>doCreateBean

类名称为:AbstractAutowireCapableBeanFactory

这里面主要看红线标注的三个方法

createBeanInstance方法,创建对象的

点击看instantiateBean=>instantiate=>BeanUtils.instantiateClass

  1. public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
  2. Assert.notNull(ctor, "Constructor must not be null");
  3. try {
  4. ReflectionUtils.makeAccessible(ctor);
  5. return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
  6. KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
  7. }
  8. catch (InstantiationException ex) {
  9. throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
  10. }
  11. catch (IllegalAccessException ex) {
  12. throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
  13. }
  14. catch (IllegalArgumentException ex) {
  15. throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
  16. }
  17. catch (InvocationTargetException ex) {
  18. throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
  19. }
  20. }
可以看到创建对象的方法是通过构造器反射创建的

如果对于创建对象的方式比较迷惑,可以看另一篇文章【java基础】Java常见的创建对象方式_风卷残云_迟来大师的博客-CSDN博客

另外两个方法,下面讲

2.1@Value创建时机

2.1.1 看这个applyMergedBeanDefinitionPostProcessors方法

这个是后置处理器,去收集/预解析属性元数据

这个方法会调用AutowiredAnnotationBeanPostProcessor类的这个postProcessMergedBeanDefinition方法,然后调用这个findAutowiringMetadata方法

findAutowiringMetadata 找到注入的元素

下面看红线的方法,这个是核心方法

  1. private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
  2. /**
  3. * 如果没有 Autowired Value 注解信息就返回 EMPTY
  4. * this.autowiredAnnotationTypes.add(Autowired.class);
  5. * this.autowiredAnnotationTypes.add(Value.class);
  6. */
  7. if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
  8. return InjectionMetadata.EMPTY;
  9. }
  10. List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
  11. Class<?> targetClass = clazz;
  12. do {
  13. final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
  14. // 遍历Class中的所有field,根据注解判断每个field是否需要被注入
  15. ReflectionUtils.doWithLocalFields(targetClass, field -> {
  16. // 看看field是不是有注解@Autowired 或 @Value
  17. MergedAnnotation<?> ann = findAutowiredAnnotation(field);
  18. if (ann != null) {
  19. // 不支持静态类
  20. if (Modifier.isStatic(field.getModifiers())) {
  21. if (logger.isInfoEnabled()) {
  22. logger.info("Autowired annotation is not supported on static fields: " + field);
  23. }
  24. return;
  25. }
  26. // 确定带注解的字段是否存在required并且是true 默认是true
  27. boolean required = determineRequiredStatus(ann);
  28. // AutowiredFieldElement 对象包装一下
  29. currElements.add(new AutowiredFieldElement(field, required));
  30. }
  31. });
  32. // 遍历Class中的所有method,根据注解判断每个method是否需要注入
  33. ReflectionUtils.doWithLocalMethods(targetClass, method -> {
  34. // 桥接方法 什么是桥接方法 大概查了查跟泛型方法有关系
  35. // 我猜的哈 比如某一个泛型方法 没有具体的实现的话 不知道注入何种类型 就会略过吧 知道的还请告知哈
  36. Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
  37. if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
  38. return;
  39. }
  40. // 看看方法是不是有注解@Autowired 或 @Value
  41. MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
  42. if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
  43. // 静态方法略过
  44. if (Modifier.isStatic(method.getModifiers())) {
  45. if (logger.isInfoEnabled()) {
  46. logger.info("Autowired annotation is not supported on static methods: " + method);
  47. }
  48. return;
  49. }
  50. // 参数为空的方法略过
  51. if (method.getParameterCount() == 0) {
  52. if (logger.isInfoEnabled()) {
  53. logger.info("Autowired annotation should only be used on methods with parameters: " +
  54. method);
  55. }
  56. }
  57. // 判断是不是有 required
  58. boolean required = determineRequiredStatus(ann);
  59. // 获取目标class中某成员拥有读或写方法与桥接方法一致的PropertyDescriptor
  60. PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
  61. // AutowiredMethodElement 对象包装一下
  62. currElements.add(new AutowiredMethodElement(method, required, pd));
  63. }
  64. });
  65. elements.addAll(0, currElements);
  66. // 递归调用
  67. targetClass = targetClass.getSuperclass();
  68. }
  69. while (targetClass != null && targetClass != Object.class);
  70. // 包装成 InjectionMetadata 对象 targetClass属性就是当前的类 injectedElements属性就是分析的字段或者方法
  71. return InjectionMetadata.forElements(elements, clazz);
  72. }

看着这个代码

AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);

找到自动注入的注解,进去看下:

  1. @Nullable
  2. private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
  3. if (ao.getAnnotations().length > 0) { // autowiring annotations have to be local
  4. for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
  5. AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
  6. if (attributes != null) {
  7. return attributes;
  8. }
  9. }
  10. }
  11. return null;
  12. }

这个里面有一个全局变量

this.autowiredAnnotationTypes

看下这个值是咋来的,查看AutowiredAnnotationBeanPostProcessor这个类的构造函数

很清晰的告诉你了,我们要看的注解

从上面可以看到这个buildAutowiringMetadata方法会对类的属性进行遍历以及父亲的递归,对于字段会忽略掉static修饰的,对于方法会也会忽略掉static以及参数为空的。最后解析到的属性会包装成 AutowiredFieldElement ,方法会包装成 AutowiredMethodElement ,最后统一放进集合中,包装成 InjectionMetadata 对象返回,并放进缓存

2.1.2看populateBean这个方法

点击去看调用这个postProcessPropertyValues方法

继续往下看

element.inject(target, beanName, pvs)方法
调用对象对应的方法进行注入 属性就是 AutowiredFieldElement  方法就是 AutowiredMethodElement

点下去主要看属性

第一个红线获取属性值

第二个红线注入属性值

2.1.3看下beanFactory.resolveDependency这个获取属性值方法

doResolveDependency这个是核心
  1. @Nullable
  2. public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
  3. @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  4. InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
  5. try {
  6. Object shortcut = descriptor.resolveShortcut(this);
  7. if (shortcut != null) {
  8. return shortcut;
  9. }
  10. Class<?> type = descriptor.getDependencyType();
  11. // 如果是 @Value 注解元素,则获取 value 值
  12. Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
  13. if (value != null) {
  14. if (value instanceof String) {
  15. // 占位符解析
  16. String strVal = resolveEmbeddedValue((String) value);
  17. BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
  18. // SpEL 解析
  19. value = evaluateBeanDefinitionString(strVal, bd);
  20. }
  21. TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
  22. return (descriptor.getField() != null ?
  23. converter.convertIfNecessary(value, type, descriptor.getField()) :
  24. converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
  25. }
  26. Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
  27. if (multipleBeans != null) {
  28. return multipleBeans;
  29. }
  30. Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
  31. if (matchingBeans.isEmpty()) {
  32. if (isRequired(descriptor)) {
  33. raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
  34. }
  35. return null;
  36. }
  37. String autowiredBeanName;
  38. Object instanceCandidate;
  39. if (matchingBeans.size() > 1) {
  40. autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
  41. if (autowiredBeanName == null) {
  42. if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
  43. return descriptor.resolveNotUnique(type, matchingBeans);
  44. }
  45. else {
  46. // In case of an optional Collection/Map, silently ignore a non-unique case:
  47. // possibly it was meant to be an empty collection of multiple regular beans
  48. // (before 4.3 in particular when we didn't even look for collection beans).
  49. return null;
  50. }
  51. }
  52. instanceCandidate = matchingBeans.get(autowiredBeanName);
  53. }
  54. else {
  55. // We have exactly one match.
  56. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
  57. autowiredBeanName = entry.getKey();
  58. instanceCandidate = entry.getValue();
  59. }
  60. if (autowiredBeanNames != null) {
  61. autowiredBeanNames.add(autowiredBeanName);
  62. }
  63. if (instanceCandidate instanceof Class) {
  64. instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  65. }
  66. Object result = instanceCandidate;
  67. if (result instanceof NullBean) {
  68. if (isRequired(descriptor)) {
  69. raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
  70. }
  71. result = null;
  72. }
  73. if (!ClassUtils.isAssignableValue(type, result)) {
  74. throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
  75. }
  76. return result;
  77. }
  78. finally {
  79. ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
  80. }
  81. }

看下resolveEmbeddedValue方法

红线就取到值了

3.结论:

经过源码的层层解析,大家已经看到了,@Value取值实在对象创建完成后,在后置处理器中进行赋值的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/496031
推荐阅读
相关标签
  

闽ICP备14008679号