SpringloC容器的依赖注入源码解析(6)—— doCreateBean之处理@Autowired以及@Value标签

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 进入到上面的applyMergedBeanDefinitionPostProcessors方法里:

文章目录

进入到上面的applyMergedBeanDefinitionPostProcessors方法里:


protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof MergedBeanDefinitionPostProcessor) {
         MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
         // 重点关注AutowiredAnnotationBeanPostProcessor,该类会把@Autowired等标记的
         // 需要依赖注入的成员变量或者方法实例给记录下来,方便后续populateBean使用
         bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
      }
   }
}


其中实现了postProcessMergedBeanDefinition方法的一个实现类MergedBeanDefinitionPostProcessor是要分析的重点


2.png


3.png


构造方法:

public AutowiredAnnotationBeanPostProcessor() {
   this.autowiredAnnotationTypes.add(Autowired.class);
   this.autowiredAnnotationTypes.add(Value.class);
   try {
      this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
            ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
      logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
   }
   catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
   }
}

把Autowired和Value标签加入到类型为Set的autowiredAnnotationTypes里。



当BeanName是自定义的WelcomeController时,从applyMergedBeanDefinitionPostProcessors方法的postProcessMergedBeanDefinition处进入断点:


@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
   metadata.checkConfigMembers(beanDefinition);
}


findAutowiringMetadata是寻找被注解的元数据:

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
   // Fall back to class name as cache key, for backwards compatibility with custom callers.
   String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
   // Quick check on the concurrent map first, with minimal locking.
   // 从容器中查找是否有给定类的autowire相关注解元信息
   InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
   if (InjectionMetadata.needsRefresh(metadata, clazz)) {
      synchronized (this.injectionMetadataCache) {
         metadata = this.injectionMetadataCache.get(cacheKey);
         if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            if (metadata != null) {
               metadata.clear(pvs);
            }
            metadata = buildAutowiringMetadata(clazz);
            // 将得到的给定类autowire相关注解源信息存储在容器缓存中
            this.injectionMetadataCache.put(cacheKey, metadata);
         }
      }
   }
   return metadata;
}


该方法扫描到类里的属性或者方法有对应的注解,就会将其封装起来,最终封装成InjectionMetadata实例返回,进入到InjectionMetadata里:

4.png


其中包含了哪些需要注入的元素,以及元素要注入到哪个目标类中,targetClass代表目标bean的class对象,injectedElements保存的是需要被注入的元素,即被Autowired或Value注解的属性或方法,checkedElements里面只保存了由Spring默认处理的属性、方法。


 injectedElements集合的泛型是InjectedElement,保存单个InjectedElement的值


5.png


member用于保存被Autowired、Value注解标记的属性,isField用来决定member是Field还是方法,PropertyDescriptor可以对属性反射读写操作


回到findAutowiringMetadata方法,cacheKey是metadata在容器缓存中的名字,获取到了之后会先在容器缓存里查一下先前有没有获取到InjectionMetadata对象,之后会执行needsRefresh方法


public static boolean needsRefresh(@Nullable InjectionMetadata metadata, Class<?> clazz) {
   return (metadata == null || metadata.targetClass != clazz);
}


该方法判断是否需要重新去扫描获取bean,如果不需要就会直接从缓存里返回先前注册的metadata实例。


在 if 里用到了双重锁检查机制,最终会调用


metadata = buildAutowiringMetadata(clazz);

创建出metadata来,进入到buildResourceMetadata:

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
   if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
      return InjectionMetadata.EMPTY;
   }
   List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
   Class<?> targetClass = clazz;
   do {
      final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
      // 收集被@Autowired或者@Value标记的Field
      // 利用反射机制获取给定类中所有的声明字段,获取字段上的注解信息
      ReflectionUtils.doWithLocalFields(targetClass, field -> {
         MergedAnnotation<?> ann = findAutowiredAnnotation(field);
         if (ann != null) {
            if (Modifier.isStatic(field.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static fields: " + field);
               }
               return;
            }
            boolean required = determineRequiredStatus(ann);
            // 将当前字段元信息封装,添加在返回的集合中
            currElements.add(new AutowiredFieldElement(field, required));
         }
      });
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
         if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
            return;
         }
         MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
         if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
            if (Modifier.isStatic(method.getModifiers())) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation is not supported on static methods: " + method);
               }
               return;
            }
            if (method.getParameterCount() == 0) {
               if (logger.isInfoEnabled()) {
                  logger.info("Autowired annotation should only be used on methods with parameters: " +
                        method);
               }
            }
            boolean required = determineRequiredStatus(ann);
            PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
            currElements.add(new AutowiredMethodElement(method, required, pd));
         }
      });
      elements.addAll(0, currElements);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);
   return InjectionMetadata.forElements(elements, clazz);
}


首先看一下该类是否有资格成为被Autowired或Value标记的候选类,主要是看一下class对象是不是普通的class,即这两个注解在这个class里有没有表示别的意思,判断逻辑如下:


public static boolean isCandidateClass(Class<?> clazz, String annotationName) {
   if (annotationName.startsWith("java.")) {
      return true;
   }
   if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) {
      return false;
   }
   return true;
}


注解有java开头的注解就算是候选者;如果clazz是以java.打头或是Order类型的,则标签不会起作用,不能算做候选者



回到AutowiredAnnotationBeanPostProcessor.java的buildAutowiringMetadata方法


6.png


之后会通过循环来遍历class里面的属性元素以及方法元素,不管是何种元素都会执行findAutowiredAnnotation方法收集被Aotowired或Value标记的Field


进入到findAutowiredAnnotation方法里:


@Nullable
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
   MergedAnnotations annotations = MergedAnnotations.from(ao);
   for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
      MergedAnnotation<?> annotation = annotations.get(type);
      if (annotation.isPresent()) {
         return annotation;
      }
   }
   return null;
}


isPresent方法判断是否被两种标签修饰,是的话将标记信息返回


7.png


之后判断Field是否是静态的,是的话会抛异常,因为静态Field不支持注入


之后再会获得Autowired里面的require属性

boolean required = determineRequiredStatus(ann);


获取到相关信息之后就会将field和required属性添加到list里面

currElements.add(new AutowiredFieldElement(field, required));

之后就会把收集到的被注解标记的属性以及方法实例,存储到集合中

8.png


最后返回:


return InjectionMetadata.forElements(elements, clazz);


进入到forElements方法里:

public static InjectionMetadata forElements(Collection<InjectedElement> elements, Class<?> clazz) {
   return (elements.isEmpty() ? InjectionMetadata.EMPTY : new InjectionMetadata(clazz, elements));
}


该方法会将收集到的被标记的元素放入到新创建的InjectionMetadata实例里


9.png


回到findAutowiringMetadata,

10.png

最后将metadata存储到缓存里


回到postProcessMergedBeanDefinition方法


@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
   metadata.checkConfigMembers(beanDefinition);
}

在获取到了metadata之后,会调用checkConfigMembers方法,进入到方法里:

public void checkConfigMembers(RootBeanDefinition beanDefinition) {
   Set<InjectedElement> checkedElements = new LinkedHashSet<>(this.injectedElements.size());
   for (InjectedElement element : this.injectedElements) {
      Member member = element.getMember();
      if (!beanDefinition.isExternallyManagedConfigMember(member)) {
         beanDefinition.registerExternallyManagedConfigMember(member);
         checkedElements.add(element);
         if (logger.isTraceEnabled()) {
            logger.trace("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
         }
      }
   }
   this.checkedElements = checkedElements;
}


会把需要spring容器用默认策略注入的injectedelements集合给保存到前面说的checkedElements里,这样就完成了收集被Autowired和Value标记的属性和方法,有了实例之后就能通过调用其inject方法在合适的时候进行属性值的注入


回到doCreateBean


11.png



相关文章
|
10天前
|
Kubernetes Linux 虚拟化
入门级容器技术解析:Docker和K8s的区别与关系
本文介绍了容器技术的发展历程及其重要组成部分Docker和Kubernetes。从传统物理机到虚拟机,再到容器化,每一步都旨在更高效地利用服务器资源并简化应用部署。容器技术通过隔离环境、减少依赖冲突和提高可移植性,解决了传统部署方式中的诸多问题。Docker作为容器化平台,专注于创建和管理容器;而Kubernetes则是一个强大的容器编排系统,用于自动化部署、扩展和管理容器化应用。两者相辅相成,共同推动了现代云原生应用的快速发展。
68 11
|
26天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
1月前
|
Android开发 开发者 Python
通过标签清理微信好友:Python自动化脚本解析
微信已成为日常生活中的重要社交工具,但随着使用时间增长,好友列表可能变得臃肿。本文介绍了一个基于 Python 的自动化脚本,利用 `uiautomator2` 库,通过模拟用户操作实现根据标签批量清理微信好友的功能。脚本包括环境准备、类定义、方法实现等部分,详细解析了如何通过标签筛选并删除好友,适合需要批量管理微信好友的用户。
67 7
|
1月前
|
负载均衡 网络协议 算法
Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式
本文探讨了Docker容器环境中服务发现与负载均衡的技术与方法,涵盖环境变量、DNS、集中式服务发现系统等方式,以及软件负载均衡器、云服务负载均衡、容器编排工具等实现手段,强调两者结合的重要性及面临挑战的应对措施。
99 3
|
2月前
|
安全 持续交付 Docker
深入理解并实践容器化技术——Docker 深度解析
深入理解并实践容器化技术——Docker 深度解析
86 2
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
1月前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
261 77
|
10天前
|
Ubuntu NoSQL Linux
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
80 6
《docker基础篇:3.Docker常用命令》包括帮助启动类命令、镜像命令、有镜像才能创建容器,这是根本前提(下载一个CentOS或者ubuntu镜像演示)、容器命令、小总结
|
1月前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
21天前
|
Ubuntu Linux 开发工具
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
Docker 是一个开源的容器化平台,允许开发者将应用程序及其依赖项打包成标准化单元(容器),确保在任何支持 Docker 的操作系统上一致运行。容器共享主机内核,提供轻量级、高效的执行环境。本文介绍如何在 Ubuntu 上安装 Docker,并通过简单步骤验证安装成功。后续文章将探讨使用 Docker 部署开源项目。优雅草央千澈 源、安装 Docker 包、验证安装 - 适用场景:开发、测试、生产环境 通过以上步骤,您可以在 Ubuntu 系统上成功安装并运行 Docker,为后续的应用部署打下基础。
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈

推荐镜像

更多