Spring 注解处理流程

2023-05-16

Spring 注解处理流程

以下内容是以springboot源码分析。

https://www.jianshu.com/p/acd1565510e3

类的注解处理流程

常用的类的注解:
@SpringBootApplication
@Controller
@Service
@Repository
@Configuration

springboot启动执行,初始上下文,扫描@SpringBootApplication注解类的以下的所有包。
SpringApplication.run()

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();//将类信息保存到beanDefinitionNames集合中,类注解被处理。
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);//实例化自定义bean,bean的变量注解被处理。
            this.afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

> SpringApplication.createApplicationContext() >BeanUtils.instantiateClass()> AnnotationConfigUtils.registerAnnotationConfigProcessors()>
AnnotationConfigUtils.registerPostProcessor()> GenericApplicationContext.registerBeanDefinition()> DefaultListableBeanFactory.registerBeanDefinition()方法中将beanname放入集合beanDefinitionNames中。

以上过程就是扫描并将组件注解的类信息保存到beanDefinitionNames集合中,再由AbstractApplicationContext.refresh()实例化自定义Bean。

变量的注解处理流程

常用的变量注解
@Autowired
@Resource
@Value()

我们自定义的Bean初始化是在AbstractApplicationContext的refresh()方法中调用finishBeanFactoryInitialization(beanFactory)完成的。
finishBeanFactoryInitialization(beanFactory) ==》beanFactory.preInstantiateSingletons() ==》getBean(beanName) ==》doGetBean ==》createBean ==》AbstractAutowireCapableBeanFactory.doCreateBean(…) ==》 instanceWrapper = this.createBeanInstance(…)
instanceWrapper 就是创建的bean实例,instanceWrapper 是一个包装实例,变量没有被赋值。

接下来AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(…) ==》
AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(…)方法将类的变量封装到InjectionMetadata的injectedElements里面去。
后面AbstractAutowireCapableBeanFactory.populateBean(…) > AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(…)>AutowiredAnnotationBeanPostProcessor.postProcessProperties(…)方法中

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);

        try {
            metadata.inject(bean, beanName, pvs);
            return pvs;
        } catch (BeanCreationException var6) {
            throw var6;
        } catch (Throwable var7) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var7);
        }
    }

找到需要注入的哪些元数据,然后metadata.inject(注入),注入方法点进去,来到InjectionMetadata的inject方法,在一个for循环里面依次执行element.inject(target, beanName, pvs),来对属性进行注入。
AutowiredAnnotationBeanPostProcessor 的内部类 AutowiredFieldElement中的inject方法如下:

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
            Field field = (Field)this.member;
            Object value;
            if (this.cached) {
                value = AutowiredAnnotationBeanPostProcessor.this.resolvedCachedArgument(beanName, this.cachedFieldValue);
            } else {
                DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
                desc.setContainingClass(bean.getClass());
                Set<String> autowiredBeanNames = new LinkedHashSet(1);
                Assert.state(AutowiredAnnotationBeanPostProcessor.this.beanFactory != null, "No BeanFactory available");
                TypeConverter typeConverter = AutowiredAnnotationBeanPostProcessor.this.beanFactory.getTypeConverter();

                try {
                    value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
                } catch (BeansException var12) {
                    throw new UnsatisfiedDependencyException((String)null, beanName, new InjectionPoint(field), var12);
                }

                synchronized(this) {
                    if (!this.cached) {
                        if (value == null && !this.required) {
                            this.cachedFieldValue = null;
                        } else {
                            this.cachedFieldValue = desc;
                            AutowiredAnnotationBeanPostProcessor.this.registerDependentBeans(beanName, autowiredBeanNames);
                            if (autowiredBeanNames.size() == 1) {
                                String autowiredBeanName = (String)autowiredBeanNames.iterator().next();
                                if (AutowiredAnnotationBeanPostProcessor.this.beanFactory.containsBean(autowiredBeanName) && AutowiredAnnotationBeanPostProcessor.this.beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                                    this.cachedFieldValue = new AutowiredAnnotationBeanPostProcessor.ShortcutDependencyDescriptor(desc, autowiredBeanName, field.getType());
                                }
                            }
                        }

                        this.cached = true;
                    }
                }
            }

            if (value != null) {
                ReflectionUtils.makeAccessible(field);
                field.set(bean, value);
            }

        }

可以看到value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(…)中,对需要注入的变量,进行校验,比较,匹配等工作,拿到实例。

方法的注解处理流程

@Cacheable
@PostMapping
@RequestMapping
@GetMapping

拿@Cacheable注解来说,beanFactory在创建bean时会给方法加上切点,设置了Interceptor,添加方法代理。
AbstractAutowireCapableBeanFactory.doCreateBean()>AbstractAutowireCapableBeanFactory.createBeanInstance()>
AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod()>ConstructorResolver.instantiateUsingFactoryMethod()>ConstructorResolver.instantiateUsingFactoryMethod()
>SimpleInstantiationStrategy.instantiate()>BeanUtils.instantiateClass()==>
==>ConfigurationClassEnhancer.intercept() ==> MethodProxy.invokeSuper()

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable {
            ConfigurableBeanFactory beanFactory = this.getBeanFactory(enhancedConfigInstance);
            String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
            if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
                String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
                if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
                    beanName = scopedBeanName;
                }
            }

            if (this.factoryContainsBean(beanFactory, "&" + beanName) && this.factoryContainsBean(beanFactory, beanName)) {
                Object factoryBean = beanFactory.getBean("&" + beanName);
                if (!(factoryBean instanceof ScopedProxyFactoryBean)) {
                    return this.enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
                }
            }

            if (this.isCurrentlyInvokedFactoryMethod(beanMethod)) {
                if (ConfigurationClassEnhancer.logger.isInfoEnabled() && BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
                    ConfigurationClassEnhancer.logger.info(String.format("@Bean method %s.%s is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.", beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
                }

                return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
            } else {
                return this.resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
            }
        }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring 注解处理流程 的相关文章

随机推荐

  • 2的幂次方

    2的幂次方 判断一个数是否为2的幂次方 我们可以参考如下链接 xff1a 判断一个数是否为2的N次方 借鉴文中的分析 xff0c 我认为 xff1a 其实一个数n xff0c 如果是2的幂次方数 xff0c 则n的二进制原码中一定只有一个1
  • 爬虫1000+个C程序

    爬虫1000 43 个C程序 问题场景 由于实验需要 xff0c 我需要1000 43 个elf文件 xff0c 可是网络可获取的elf文件较少 xff0c c程序较多 xff0c 所以首先下载c程序 xff0c 之后gcc编译链接生成el
  • 【libuv】定时器:uv_timer_start 的timeout和repeat 参数

    花了好几个小时验证 uv timer start 发现怎么都是错误的 现象是 repeat 作为了timeout的效果 而且repeat 代表是毫秒级别的 按照原有设计 timeout是微秒 repeat的参数并不是timeout参数 而且
  • 解决IDEA Maven依赖下载总是失败的几种方法

    为啥我的IDEA Maven依赖下载总是失败 xff1f xff1f 我们本地使用 IDEA 运行 maven 项目的时候 xff0c 有时候运气不好 xff0c 就会遇到某些 maven 依赖无法正常找到 导入 这就会导致 IDEA 构建
  • 几种常见排序算法的实现及运行时间对比

    冒泡排序 function popSort arr if arr length lt 2 return arr let n 61 arr length while n gt 1 let exchanged 61 false for let
  • FtpClient切换、创建目录不成功的问题(makeDirectory、changeWorkingDirectory)

    FtpClient切换 创建目录不成功的问题 xff08 makeDirectory changeWorkingDirectory xff09 做功能时切换目录老是不成功且无法创建文件夹 xff0c 翻阅一些博客后发现Ftp创建目录只能逐级
  • RNA-seq数据上游分析流程(从原始数据开始)

    数据分析的基本思路 xff08 1 xff09 从ncbi的geo或者其它数据库中查找自己感兴趣的RNASeq数据 xff0c 至少要求给出如下信息 xff1a xff08 2 xff09 对芯片数据进行质量控制评价及处理 xff08 如果
  • R语言2——数据类型和基本运算

    R语言2 数据类型和基本运算 目录 R语言2 数据类型和基本运算 1 R的数据类型 1 R的数据类型 xff08 1 xff09 Logical xff08 逻辑型 xff09 xff1a 只有两个值TRUE FALSE xff08 2 x
  • 红芯对chromium,瞩目对zoom。扒扒瞩目客户端。

    0x00 前提 一直以来 xff0c 人们普遍怀疑瞩目等 国产 云会议系统在界面 xff0c 功能上有剽窃国外的Zoom us的嫌疑 xff0c 但无证据 本文从瞩目的Ubuntu客户端入手 xff0c 详细证明了瞩目的Zoom血统 0x0
  • Linux下微信终极解决方案(不是网页版/electronic wechat)

    文章目录 为什么有这篇文章 xff1f 所需工具本人环境步骤0x01 解压微信安装包0x02 安装 Wine制作启动器 目前发现的Bugs 为什么有这篇文章 xff1f 在Linux系统下 xff0c 微信的使用体验说实话很糟糕 为新网页版
  • 发现了一个好玩的百科——BetaWorld

    官网说明 BetaWorld 百科是一部关于 Windows Beta 的综合百科全书 希望您能够通过这部百科全书 xff0c 更加深入地了解 Windows 操作系统的发展历程 xff0c 积累丰富的 Windows Beta 知识 网址
  • ArchLinux 应用清单

    说明 本清单是本人根据个人经验所写的 great 应用程序列表 欢迎在评论区补充 xff5e xff5e 基本类 安装时的base不再赘述了 xff5e 命令行工具 ZSH ShellOh My ZSH ZSH 美化 工具htop 替代过时
  • Radare2 学习笔记:从入门到精通 1. Radare2 简介,及安装

    目录 Radare2 是什么 xff1f Radare2 介绍如何安装 Radare2 xff1f 在 Linux 中安装 Radare2 和 Cutter在Mac OSX 中安装 R2 和Cutter在 Microsoft Windows
  • 【webrtc】web端打开日志及调试

    参考gist Chrome Browser debug logs sawbuck webrtc org native code logging 取日志 C Users zhangbin AppData Local Google Chrome
  • 使用Fedora Server和Nextcloud,自己动手搭建私有云

    文章目录 这篇文章要干什么 xff1f 刻录 Fedora Server 安装盘安装 Fedora ServerFedora Server 软件源配置使用 OpenSSH 服务使用PuTTY轻松登入下载 Nextcloud安装 Nextcl
  • Foxit PDF 福昕PDF 阅读器下载慢怎么办

    目录 修改CDN2 5 MB S 秒下 xff01 修改CDN 原地址 xff1a http cdn01 foxitsoftware com pub foxit reader desktop linux 2 x 2 4 en us Foxi
  • QEMU 使用手册(英文)

    获取方法 xff1a span class token function man span qemu span class token operator span multimarkdown span class token operato
  • Sql语句中LIKE的嵌套用法详解

    Oracle中Sql语句LIKE语句的语法格式是 xff1a select from 表名 where 字段名 like 对应值 xff08 子串 xff09 例 xff1a 查询name字段中含有 林 字的 select from 表名
  • SpringBoot注解最全详解(整合超详细版本)

    使用注解的优势 xff1a 1 采用纯java代码 xff0c 不在需要配置繁杂的xml文件 2 在配置中也可享受面向对象带来的好处 3 类型安全对重构可以提供良好的支持 4 减少复杂配置文件的同时亦能享受到springIoC容器提供的功能
  • Spring 注解处理流程

    Spring 注解处理流程 以下内容是以springboot源码分析 https www jianshu com p acd1565510e3 类的注解处理流程 常用的类的注解 xff1a 64 SpringBootApplication