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

目录
  • springboot @Value获取绝对路径文件的内容
    • 默认情况下使用
    • 同样的我们可以使用file协议获取文本的内容
  • Spring注解@Value解读
    • 依赖注入概述
    • 实际应用案例
    • 源码解读分析
      • 1、首先进入SpringApplication.run(XXXApplication.class, args)
      • 2、然后进入到AbstractApplicationContext的refresh()方法
      • 3、而后进入到DefaultListableBeanFactory的preInstantiateSingletons()方法
      • 4、然后我们进入到AbstractAutowireCapableBeanFactory
      • 5、我们略过创建实例的过程
      • 6、第5步完成了AbstractAutowireCapableBeanFactory

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实现。

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

(0)

相关推荐

  • SpringBoot 使用 @Value 注解读取配置文件给静态变量赋值

    1.application.properties 配置文件 mail.username=xue@163.com mail.password=xue mail.host=smtp.163.com mail.smtp.auth=true 2.给普通变量赋值,直接在变量上添加 @Value 注解 import org.springframework.beans.factory.annotation.Value; public class MailConfig { @Value("${mail.user

  • springboot如何使用@Value获取配置文件的值

    使用@Value获取配置文件的值 1.创建配置文件(application.properties) spring.activemq.broker-url=tcp://localhost:61616 spring.activemq.user=admin spring.activemq.password=admin spring.activemq.in-memory=true spring.activemq.pool.enabled=false 2.创建测试类(MyController.java)

  • Springboot中@Value的使用详解

    Springboot通过@Value注解将配置文件中的属性注入到容器内组件中(可用在@Controller.@Service.@Configuration.@Component等Spring托管的类中) 1.普通字符串注入 例:yml中存在key: name: zs @Value注入 @Value("${name}") public String name; 当yml中的name没有对应值时,即yml中为: name: 此时字符串name的值为"" 可设置注入属性的

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

    目录 springboot @Value获取绝对路径文件的内容 默认情况下使用 同样的我们可以使用file协议获取文本的内容 Spring注解@Value解读 依赖注入概述 实际应用案例 源码解读分析 1.首先进入SpringApplication.run(XXXApplication.class, args) 2.然后进入到AbstractApplicationContext的refresh()方法 3.而后进入到DefaultListableBeanFactory的preInstantiat

  • java读取html文件,并获取body中所有的标签及内容的案例

    这里的获取的是html文件中body中的所有标签以及内容 package com.lmt.service.file; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.Reader; import org.springframework.stereotype.Component; i

  • Android 通过API获取数据库中的图片文件方式

    Overview 今天复习了一下Android 如何将我们数据库中图片获取出来,并且将其转换为bitmap进行显示. 开发环境以及技术 使用Visual Studio 2019 Android Studio 3.5 API 使用 ASP .NET WEB API 开发 数据库操作只用Entity Framework 使用本地数据库作为数据源 如果你是一个需要开发软件和API的程序员,那么你可以看一下,如果你不是可以选择跳过 $\color{#6995C2}{API开发}$. API 开发 这里我

  • SpringBoot如何配置获取request中body的json格式参数

    目录 背景 获取请求中的参数(非json格式参数) 获取方法 结论 获取POST请求json格式的参数 经过检索推荐方法(参看后边完整方法) 实现方法 使用 背景 最近开发项目,因为有第三方调用我们的接口,我们使用SpringBoot以JavaBean的方式接收了我们预期的参数,参数接收也没有什么异常.但是有一些需求问题需要沟通,需要拿到合作第三方传入的所有参数,来进行参数核验. 如何拿到请求的所有参数呢?正常的思路肯定是从request中获取,如果是GET请求,参数在请求路径中拼接:如果是PO

  • 使用springboot logback动态获取application的配置项

    目录 springboot logback动态获取application的配置项 logback自身支持以下方式配置 因此只能使用spring提供的标签来对此参数进行配置 springboot logback配置及动态配置log目录 子节点一 appender 控制台输出appender 文件输入appender root节点-启用配置 动态配置log目录 获取jar目录路径 springboot logback动态获取application的配置项 在多环境的情况下,logback的日志路径需

  • Django 如何获取前端发送的头文件详解(推荐)

    Django里面,我们知道可以在函数里面里面通过参数获取前端发来的数据. 比如可以通过request获取各种form的值啊,cookie啊等等. def index(reqeust): # 获取当前已经登录的用户 v = reqeust.COOKIES.get('username111') return render(reqeust,'index.html',{'current_user': v}) 这些方法是Django把一些常用的数据已经替我们封装好了,因此可以直接使用.但是这些方法并不全,

  • springboot如何获取相对路径文件夹下静态资源的方法

    今日遇到一个问题:springboot需要获取到一个自定义名称文件夹下的静态资源(图片等),并且文件夹的路径不在classPath下面,而是一个相对路径. 一开始使用修改配置文件的方式: # 配置静态资源访问前缀 spring.mvc.static-path-pattern=*/** # 配置静态资源路径,默认配置失效 spring.resources.static-locations=../upload 发现并不行,无法解析出相对路径. 后面通过自定义静态资源映射配置类实现了: @Config

  • SpringBoot如何获取src/main/resource路径下的文件

    目录 SpringBoot获取src/main/resource路径下的文件 SpringBoot打成jar包后,读取resources目录下的文件 一般方法 正确方法 SpringBoot获取src/main/resource路径下的文件 下面的ResourceUtils使用spring-core的工具包,在org.springframework.util包下 File file = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFI

  • C#获取某路径文件夹中全部图片或其它指定格式的文件名的实例方法

    1.编写获取文件名(全路径)子函数 /////param ///path:文件夹路径 ///suffix:后缀格式, 如bmp,txt ///fileList:文件名存放 ///isSubcatalog:true遍历子文件夹,否则不遍历 void getFiles(string path, string suffix, ref List<string> fileList, bool isSubcatalog) { string filename; DirectoryInfo dir = new

  • SpringBoot普通类获取spring容器中bean的操作

    前言 在spring框架中,是无法在普通类中通过注解注入实例的,因为sping框架在启动的时候,就会将标明交给spring容器管理的类进行实例化,并梳理他们彼此的依赖关系,进行注入,没有交给spring容器管理的普通类,是不会进行注入的,即使你使用了注入的相关注解.这个时候,如果我们需要在普通类中获取spring容器中的实例,就需要一些特定的方法,这里将整理一下如何在springboot中实现这样的方法. 创建springboot工程demo 项目结构图示 项目结构说明 service包下为de

随机推荐