我的编程空间,编程开发者的网络收藏夹
学习永远不晚

springboot @Value实现获取计算机中绝对路径文件的内容

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

springboot @Value实现获取计算机中绝对路径文件的内容

springboot @Value获取绝对路径文件的内容

默认情况下使用


@Value("aaa.txt")
private Resource txtResource;

这样获取到的是项目classpath 下的 aaa.txt

如果想获取非项目路径下的文件内容怎么办呢,看了下@Value的好像也没有说,

其实


@Value("https://www.baidu.com")
private Resource urlResource;

这样是可以获取到 百度首页的内容的.它这里使用的是https协议.

同样的我们可以使用file协议获取文本的内容

即:


@Value("file:///E://aaa.txt")
private Resource txtResource;

使用@Value 有一个好处就是,你不用关心文本内容的变化,你每次调用的时候,springboot 会自动帮你重新加载.

Spring注解@Value解读

主要通过源码解读来分析@Value实现属性注入Spring Bean的过程,并对static类型字段无法通过@Value注入为Spring Bean依赖的原因做一个探究。

依赖注入概述

基于Spring MVC或者Spring Boot开发后端项目的时候总是绕不开Spring IOC容器,Spring IOC容器管理Spring Bean,我们可以通过XML或者注解的方式来定义一个Bean,如通过注解@Service,@Controller,@Component,@Repository或者@Bean加Java配置方式。实际应用中我们定义的一个Bean很多时候存在互相之间的依赖,比如Service层通过@Service定义的Bean往往要依赖数据库DAO层通过@Repository定义的Bean,这时候我们往往通过@Autowired或@Resource来自动装配,另外还有一些Bean内部的属性(Field)需要通过配置文件中定义的值来设置,而后创建的Bean才是符合我们预期的,我们一般通过@Value和@ConfigurationProperties来实现属性的注入。

实际应用案例

以文档管理服务为例,我们有个资源上传的接口,其依赖一个FeignClient客户端Bean,一个业务逻辑处理的Service Bean,还依赖一个资源上传的OSS的桶bucket成员变量,bucket和OSS我们通常配置在配置文件中以区分不同环境。具体的依赖关系如下图:

源码解读分析

首先看一下Spring Bean的的整个生命周期:

在实例化对象完成后,设置属性值(polulateBean)之前,会搜集类上的注解元数据信息,然后在polulalteBean中拦截,执行BeanPostProcessor中的方法,反射注入依赖的值。@Resource是jdk提供的注解,其使用的后置处理器是CommonAnnotationBeanPostProcessor,而@Autowired和@Value注解使用的后置处理器是AutowiredAnnotationBeanPostProcessor,具体的我们下面会通过代码来解读。

先来看一下BeanFactory和ApplicationContext在装载bean时候的区别:

  • BeanFactory在启动时不会去实例化Bean,当从容器中拿Bean的时候才会去实例化;
  • ApplicationContext在启动时就把所有的Bean全部实例化了。可以为Bean配置lazy-init=true来让Bean延迟实例化。

我们下面以Spring Boot和ApplicationContext来走读一下源码:

1、首先进入SpringApplication.run(XXXApplication.class, args)

然后一直进入到SpringApplication的refreshContext()方法,refreshContext()是IOC容器初始化的核心方法,完成容器上下文的刷新,主要包括各种处理器、监听器以及Bean的初始化等工作。

2、然后进入到AbstractApplicationContext的refresh()方法

中的finishBeanFactoryInitialization(beanFactory)方法,这个方法就是ApplicationContext完成Spring Bean实例化和初始化的关键方法。

3、而后进入到DefaultListableBeanFactory的preInstantiateSingletons()方法

此方法就是获取到所有的beanName,然后循环的去实例化+初始化bean,而实例化+初始化的方式就是调用其getBean(String name)方法,因此我们直接调到getBean(name) ,此方法的最终实现是在AbstractBeanFactory中,我们进入AbstractBeanFactory的getBean(beanName)查看,然后根据调用栈直接进入到AbstractBeanFactory的doGetBean(final String name, @Nullable final Class requiredType, @Nullable final Object[] args, boolean typeCheckOnly)方法中。这个方法代码较多,我们直接查看我们要关注的核心代码段如下:

4、然后我们进入到AbstractAutowireCapableBeanFactory

createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法中,其中AbstractAutowireCapableBeanFactory是AbstractBeanFactory的一个子类,AbstractBeanFactory的createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)由AbstractAutowireCapableBeanFactory实现。createBean方法的核心是调用了AbstractAutowireCapableBeanFactory的doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args),我们直接查看doCreateBean方法。

5、我们略过创建实例的过程

直接进入applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)方法。

5.1、applyMergedBeanDefinitionPostProcessors方法

主要是循环所有后置处理器对beanDefinition做处理,我们之前说过@Value注解是通过AutowiredAnnotationBeanPostProcessor来处理的,我们直接进入AutowiredAnnotationBeanPostProcessor的postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)方法如下:

其中的InjectionMetadata如下,它主要封装了bean的全限定类名和这个类中所有带有注解的成员属性(字段或者其他bean)描述(PropertyDescriptor,可以简单理解为带注解的成员属性和注解本身两者的组合),当然也就包括由@Value注解的字段。

5.2、我们继续进入到

findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)方法,通过buildAutowiringMetadata(final Class<?> clazz)首先获取了当前bean所属类的属性注解元数据,然后存储到了injectionMetadataCache缓存中,完成了applyMergedBeanDefinitionPostProcessors方法。

5.3、我们进入到

buildAutowiringMetadata(final Class<?> clazz)查看构建bean注入注解元数据的过程

其中用到了获取当前成员属性AnnotationAttributes的方法findAutowiredAnnotation(AccessibleObject ao)如下:

其中autowiredAnnotationTypes表示后置处理器AutowiredAnnotationBeanPostProcessor处理的注解列表,初始化如下:

6、第5步完成了AbstractAutowireCapableBeanFactory

doCreateBean方法中的applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName),即完成了bean的成员属性注解元数据的本地缓存。可以理解applyMergedBeanDefinitionPostProcessors为一个拦截器,即在对bean进行成员属性和依赖注入前先预处理bean的依赖和成员属性数据的解析和缓存,而后再进行populateBean。

6.1、进入到AbstractAutowireCapableBeanFactory

populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)方法,我们直接看核心代码如下,其中postProcessProperties方法就是完成了bean的成员属性的初始化和依赖的注入过程。

6.2、因为我们要处理的是@Value和@Autowired注解的bean依赖

AutowiredAnnotationBeanPostProcessor是InstantiationAwareBeanPostProcessor接口的实现类,所以上面代码端循环过程中会进入到之前的AutowiredAnnotationBeanPostProcessor的postProcessProperties(PropertyValues pvs, Object bean, String beanName)。

6.3、上面findAutowiringMetadata方法就是第5.2步的方法

也就是从缓存Map中取出bean对应的注解元数据,而后调用InjectionMetadata的inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs)方法执行依赖注入过程。

6.4、在上面对注解元数据列表循环处理(bean依赖了多个其他的bean或者多个成员属性通过配置注入)进行依赖注入

调用element.inject(target, beanName, pvs)方法。而AutowiredAnnotationBeanPostProcessor类的内部类AutowiredFieldElement继承了InjectionMetadata.InjectedElement类,并重写了其inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法,AutowiredAnnotationBeanPostProcessor类的内部类AutowiredMethodElement也继承了InjectionMetadata.InjectedElement类,并重写了其inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法,因此6.3中的inject调用将会在此子类中执行,AutowiredFieldElement处理成员属性上注解,AutowiredMethodElement处理方法上注解。因此@Value注解的处理将会进入到AutowiredFieldElement的inject方法。

6.5、首先通过beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)方法解析依赖

然后注册当前bean依赖的其他bean,最后通过反射将依赖注入到当前bean中完成bean的依赖注入过程。DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory类,我们先查看DefaultListableBeanFactory的resolveDependency方法,其中主要是调用了DefaultListableBeanFactory的doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter)方法,我们直接看doResolveDependency方法。

6.6、以上解析@Value注解

getSuggestedValue返回的值就是${xxx.documentcenter.bucketName:ssdocumentcenterstatic},因为是字符串,所以进入到下面的逻辑AbstractBeanFactory的resolveEmbeddedValue(@Nullable String value)方法中。resolveEmbeddedValue将完成@Value注解值的解析过程,最终得到配置文件中配置的值ssdocumentcenterstatic。下面的TypeConverter的convertIfNecessary主要完成数据类型的转换,比如配置文件中配置的Boolean类型,将String转为Boolean等。最后返回value值并注入到bean中,此成员属性注入到bean的过程就完成了。我们简单看一下resolveEmbeddedValue解析过程,主要通过AbstractPropertyResolver解析器完成,其内部使用了默认的占位符如下:

通过其doResolvePlaceholders(String text, PropertyPlaceholderHelper helper)方法完成解析,内部调用了PropertyPlaceholderHelper的parseStringValue(String value, PlaceholderResolver placeholderResolver, Set visitedPlaceholders)方法来具体实现注解的值的解析和占位替换。解析首先是从配置文件对应的PropertySource中完成配置和值读取,最终通过MapPropertySource实现。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

springboot @Value实现获取计算机中绝对路径文件的内容

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java实现动态获取文件的绝对路径

我们知道在 Java 中读取一些配置文件信息,是在开发中十分常用的要求。这篇文章就来和大家聊聊Java如何实现动态获取文件的绝对路径,感兴趣的可以了解一下
2023-02-22

Java如何实现动态获取文件的绝对路径

本文小编为大家详细介绍“Java如何实现动态获取文件的绝对路径”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java如何实现动态获取文件的绝对路径”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。前言我们知道在 J
2023-07-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录