Spring框架九大核心功能全面揭秘(一)

目录

资源管理

Java资源管理

1、来个Demo

2、原理

Spring资源管理

1、资源抽象

Resource                

WritableResource

2、资源加载

3、小结

环境

1、Environment

2、配置属性源PropertySource

3、SpringBoot是如何解析配置文件

类型转换

1、类型转换API

1.1、PropertyEditor

1.2、Converter

1.3、GenericConverter

1.4、ConversionService

1.5、TypeConverter

小结

2、Environment中到底是如何进行类型转换的?


资源管理

资源管理是Spring的一个核心的基础功能,不过在说Spring的资源管理之前,先来简单说一下Java中的资源管理。

Java资源管理

Java中的资源管理主要是通过java.net.URL来实现的,通过URL的openConnection方法可以对资源打开一个连接,通过这个连接读取资源的内容。

资源不仅仅指的是网络资源,还可以是本地文件、一个jar包等等。

1、来个Demo

举个例子,比如你想到访问www.baidu.com这个百度首页网络资源,那么此时就可以这么写

public class JavaResourceDemo {public static void main(String[] args) throws IOException {//构建URL 指定资源的协议为http协议URL url = new URL("http://www.baidu.com");//打开资源连接URLConnection urlConnection = url.openConnection();//获取资源输入流InputStream inputStream = urlConnection.getInputStream();//通过hutool工具类读取流中数据String content = IoUtil.read(new InputStreamReader(inputStream));System.out.println(content);}}

解释一下上面代码的意思:

  • 首先构建一个URL,指定资源的访问协议为http协议

  • 通过URL打开一个资源访问连接,然后获取一个输入流,读取内容

运行结果

成功读取到百度首页的数据。

当然,也可以通过URL访问本地文件资源,在创建URL的时候只需要指定协议类型为file://和文件的路径就行了

URL url = new URL("file://" + "文件的路径");

这种方式这里我就不演示了。

其实这种方式实际上最终也是通过FileInputStream来读取文件数据的,不信你可以自己debug试试。

2、原理

每种协议的URL资源都需要一个对应的一个URLStreamHandler来处理。

比如说,http://协议有对应的URLStreamHandler的实现,file://协议的有对应的URLStreamHandler的实现。

Java除了支持http://file://协议之外,还支持其它的协议,如下图所示:

对于的URLStreamHandler如下图所示

当在构建URL的时候,会去解析资源的访问协议,根据访问协议找到对应的URLStreamHandler的实现。

当然,除了Java本身支持的协议之外,我们还可以自己去扩展这个协议,大致只需要两步即可:

  • 实现URLConnection,可以通过这个连接读取资源的内容

  • 实现URLStreamHandler,通过URLStreamHandler可以获取到URLConnection

不过需要注意的是,URLStreamHandler的实现需要放在sun.net.www.protocol.协议名称包下,类名必须是Handler,这也是为什么截图中的实现类名都叫Handler的原因。

当然如果不放在指定的包下也可以,但是需要实现java.net.URLStreamHandlerFactory接口。

对于扩展我就不演示了,如果你感兴趣可以自行谷歌一下。

Spring资源管理

虽然Java提供了标准的资源管理方式,但是Spring并没有用,而是自己搞了一套资源管理方式。

1、资源抽象

在Spring中,资源大致被抽象为两个接口

  • Resource:可读资源,可以获取到资源的输入流

  • WritableResource:读写资源,除了资源输入流之外,还可以获取到资源的输出流

Resource                

Resource接口继承了InputStreamSource接口,而InputStreamSource接口可以获取定义了获取输入流的方法

WritableResource

WritableResource继承了Resource接口,可以获取到资源的输出流,因为有的资源不仅可读,还可写,就比如一些本地文件的资源,往往都是可读可写的

Resource的实现很多,这里我举几个常见的:

  • FileSystemResource:读取文件系统的资源

  • UrlResource:前面提到的Java的标准资源管理的封装,底层就是通过URL来访问资源

  • ClassPathResource:读取classpath路径下的资源

  • ByteArrayResource:读取静态字节数组的数据

比如,想要通过Spring的资源管理方式来访问前面提到百度首页网络资源,就可以这么写

//构建资源
Resource resource = new UrlResource("http://www.baidu.com");
//获取资源输入流
InputStream inputStream = resource.getInputStream();

如果是一个本地文件资源,那么除了可以使用UrlResource,也可以使用FileSystemResource,都是可以的。

2、资源加载

虽然Resource有很多实现,但是在实际使用中,可能无法判断使用具体的哪个实现,所以Spring提供了ResourceLoader资源加载器来根据资源的类型来加载资源。

通过getResource方法,传入一个路径就可以加载到对应的资源,而这个路径不一定是本地文件,可以是任何可加载的路径。

ResourceLoader有个唯一的实现DefaultResourceLoader

比如对于上面的例子,就可以通过ResourceLoader来加载资源,而不用直接new具体的实现了

//创建ResourceLoader
ResourceLoader resourceLoader = new DefaultResourceLoader();
//获取资源
Resource resource = resourceLoader.getResource("http://www.baidu.com");

除了ResourceLoader之外,还有一个ResourcePatternResolver可以加载资源

ResourcePatternResolver继承了ResourceLoader

通过ResourcePatternResolver提供的方法可以看出,他可以加载多个资源,支持使用通配符的方式,比如classpath*:,就可以加载所有classpath的资源。

ResourcePatternResolver只有一个实现PathMatchingResourcePatternResolver

3、小结

到这就讲完了Spring的资源管理,这里总结一下本节大致的内容

Java的标准资源管理:

  • URL

  • URLStreamHandler

Spring的资源管理:

  • 资源抽象:Resource 、WritableResource

  • 资源加载:ResourceLoader 、ResourcePatternResolver

Spring的资源管理在Spring中用的很多,比如在SpringBoot中,application.yml的文件就是通过ResourceLoader加载成Resource,之后再读取文件的内容的。

环境

上一节末尾举的例子中提到,SpringBoot配置文件是通过ResourceLoader来加载配置文件,读取文件的配置内容

那么当配置文件都加载完成之后,这个配置应该存到哪里,怎么能够读到呢?

这就引出了Spring框架中的一个关键概念,环境,它其实就是用于管理应用程序配置的。

1、Environment

Environment就是环境抽象出来的接口

Environment继承PropertyResolver

public interface PropertyResolver {boolean containsProperty(String key);String getProperty(String key);<T> T getProperty(String key, Class<T> targetType);<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;String resolvePlaceholders(String text);}

如上是PropertyResolver提供的部分方法,这里简单说一下上面方法的作用

  • getProperty(String key),很明显是通过配置的key获取对应的value值

  • getProperty(String key, Class<T> targetType),这是获取配置,并转换成对应的类型,比如你获取的是个字符串的"true",这里就可以给你转换成布尔值的true,具体的底层实现留到下一节讲

  • resolvePlaceholders(String text),这类方法可以处理${...}占位符,也就是先取出${...}占位符中的key,然后再通过key获取到值

所以Environment主要有一下几种功能:

  • 根据key获取配置

  • 获取到指定类型的配置

  • 处理占位符

来个demo

先在application.yml的配置文件中加入配置

测试代码如下

@SpringBootApplication
public class EnvironmentDemo {public static void main(String[] args) {ConfigurableApplicationContext applicationContext = SpringApplication.run(EnvironmentDemo.class, args);//从ApplicationContext中获取到ConfigurableEnvironmentConfigurableEnvironment environment = applicationContext.getEnvironment();//获取name属性对应的值String name = environment.getProperty("name");System.out.println("name = " + name);}}

启动应用,获取到ConfigurableEnvironment对象,再获取到值

ConfigurableEnvironment是Environment子接口,通过命名也可以知道,他可以对Environment进行一些功能的配置。

运行结果:

name = java日记

2、配置属性源PropertySource

PropertySource是真正存配置的地方,属于配置的来源,它提供了一个统一的访问接口,使得应用程序可以以统一的方式获取配置获取到属性。

来个简单demo

public class PropertySourceDemo {public static void main(String[] args) {Map<String, Object> source = new HashMap<>();source.put("name", "java日记");PropertySource<Map<String, Object>> propertySource = new MapPropertySource("myPropertySource", source);Object name = propertySource.getProperty("name");System.out.println("name = " + name);}}

简单说一下上面代码的意思

  • 首先创建了一个map,就是配置来源,往里面添加了一个配置key-value

  • 创建了一个PropertySource,使用的实现是MapPropertySource,需要传入配置map,所以最终获取到属性不用想就知道是从map中获取的

最后成获取到属性

除了MapPropertySource之外,还有非常多的实现

比如CommandLinePropertySource,它其实就封装了通过命令启动时的传递的配置参数

既然PropertySource才是真正存储配置的地方,那么Environment获取到的配置真正也就是从PropertySource获取的,并且他们其实是一对多的关系

其实很好理解一对多的关系,因为一个应用程序的配置可能来源很多地方,比如在SpringBoot环境底下,除了我们自定义的配置外,还有比如系统环境配置等等,这些都可以通过Environment获取到

当从Environment中获取配置的时候,会去遍历所有的PropertySource,一旦找到配置key对应的值,就会返回

所以,如果有多个PropertySource都含有同一个配置项的话,也就是配置key相同,那么获取到的配置是从排在前面的PropertySource的获取的

这就是为什么,当你在配置文件配置username属性时获取到的却是系统变量username对应的值,因为系统的PropertySource排在配置文件对应的PropertySource之前

3、SpringBoot是如何解析配置文件

SpringBoot是通过PropertySourceLoader来解析配置文件的

load方法的第二个参数就是我们前面提到的资源接口Resource

通过Resource就可以获取到配置文件的输入流,之后就可以读取到配置文件的内容,再把配置文件解析成多个PropertySource,之后把PropertySource放入到Environment中,这样我们就可以通过Environment获取到配置文件的内容了。

PropertySourceLoader默认有两个实现,分别用来解析propertiesyml格式的配置文件

此时,上面的图就可以优化成这样

类型转换

在上一节介绍Environment时提到了它的getProperty(String key, Class<T> targetType)可以将配置的字符串转换成对应的类型,那么他是如何转换的呢?

这就跟本文要讲的Spring类型转换机制有关了

1、类型转换API

Spring类型转换主要涉及到以下几个api:

  • PropertyEditor

  • Converter

  • GenericConverter

  • ConversionService

  • TypeConverter

接下来我会来详细介绍这几个api的原理和他们之间的关系。

1.1、PropertyEditor

PropertyEditor并不是Spring提供的api,而是JDK提供的api,他的主要作用其实就是将String类型的字符串转换成Java对象属性值。

public interface PropertyEditor {void setValue(Object value);Object getValue();String getAsText();void setAsText(String text) throws java.lang.IllegalArgumentException;}

就拿项目中常用的@Value来举例子,当我们通过@Value注解的方式将配置注入到字段时,大致步骤如下图所示:

  • 取出@Value配置的key

  • 根据@Value配置的key调用Environment的resolvePlaceholders(String text)方法,解析占位符,找到配置文件中对应的值

  • 调用PropertyEditor将对应的值转换成注入的属性字段类型,比如注入的字段类型是数字,那么就会将字符串转换成数字

在转换的过程中,Spring会先调用PropertyEditor的setAsText方法将字符串传入,然后再调用getValue方法获取转换后的值。

Spring提供了很多PropertyEditor的实现,可以实现字符串到多种类型的转换

在这么多实现中,有一个跟我们前面提到的Resource有关的实现ResourceEditor,它是将字符串转换成Resource对象

也就是说,可以直接通过@Value的方式直接注入一个Resource对象,就像下面这样

@Value("http://www.baidu.com")
private Resource resource;

其实归根到底,底层也是通过ResourceLoader来加载的,这个结论是不变的。

所以,如果你想知道@Value到底支持注入哪些字段类型的时候,看看PropertyEditor的实现就可以了,当然如果Spring自带的都不满足你的要求,你可以自己实现PropertyEditor,比如把String转成Date类型,Spring就不支持。

1.2、Converter

由于PropertyEditor局限于字符串的转换,所以Spring在后续的版本中提供了叫Converter的接口,他也用于类型转换的,相比于PropertyEditor更加灵活、通用

Converter是个接口,泛型S是被转换的对象类型,泛型T是需要被转成的类型。

同样地,Spring也提供了很多Converter的实现

这些主要包括日期类型的转换和String类型转换成其它的类型

1.3、GenericConverter

GenericConverter也是类型转换的接口

这个接口的主要作用是可以处理带有泛型类型的转换,主要的就是面向集合数组转换操作,从Spring默认提供的实现就可以看出

那Converter跟GenericConverter有什么关系呢?

这里我举个例子,假设现在需要将将源集合Collection<String>转换成目标集合Collection<Date>

假设现在有个String转换成Date类型的Converter,咱就叫StringToDateConverter,那么整个转换过程如下:

  • 首先会找到GenericConverter的一个实现CollectionToCollectionConverter,从名字也可以看出来,是将一个几个转换成另一个集合

  • 然后遍历源集合Collection<String>,取出元素

  • 根据目标集合泛型Date,找到StringToDateConverter,将String转换成Date,将转换的Date存到一个新的集合

  • 返回这个新的集合,这样就实现了集合到集合的转换

所以通过这就可以看出Converter和GenericConverter其实是依赖关系

1.4、ConversionService

对于我们使用者来说,不论是Converter还是GenericConverter,其实都是类型转换的,并且类型转换的实现也很多,所以Spring为了方便我们使用Converter还是GenericConverter,提供了一个门面接口ConversionService

我们可以直接通过ConversionService来进行类型转换,而不需要面向具体的Converter或者是GenericConverter

ConversionService有一个基本的实现GenericConversionService

同时GenericConversionService还实现了ConverterRegistry的接口

ConverterRegistry提供了对Converter和GenericConverter进行增删改查的方法。

这样就可以往ConversionService中添加Converter或者是GenericConverter了,因为最终还是通过Converter和GenericConverter来实现转换的

但是我们一般不直接用GenericConversionService,而是用DefaultConversionService或者是ApplicationConversionService(SpringBoot环境底下使用)

因为DefaultConversionService和ApplicationConversionService在创建的时候,会添加很多Spring自带的Converter和GenericConverter,就不需要我们手动添加了。

1.5、TypeConverter

TypeConverter其实也是算是一个门面接口,他也定义了转换方法

他是将PropertyEditor和ConversionService进行整合,方便我们同时使用PropertyEditor和ConversionService

convertIfNecessary方法会去调用PropertyEditor和ConversionService进行类型转换,值得注意的是,优先使用PropertyEditor进行转换,如果没有找到对应的PropertyEditor,会使用ConversionService进行转换

TypeConverter有个简单的实现SimpleTypeConverter,这里来个简单的demo

public class TypeConverterDemo {public static void main(String[] args) {SimpleTypeConverter typeConverter = new SimpleTypeConverter();//设置ConversionServicetypeConverter.setConversionService(DefaultConversionService.getSharedInstance());//将字符串"true"转换成Boolean类型的trueBoolean b = typeConverter.convertIfNecessary("true", Boolean.class);System.out.println("b = " + b);}}

这里需要注意,ConversionService需要我们手动设置,但是PropertyEditor不需要,因为SimpleTypeConverter默认会去添加PropertyEditor的实现。

小结

到这就讲完了类型转换的常见的几个api,这里再简单总结一下:

  • PropertyEditor:String转换成目标类型

  • Converter:用于一个类型转换成另一个类型

  • GenericConverter:用于处理泛型的转换,主要用于集合

  • ConversionService:门面接口,内部会调用Converter和GenericConverter

  • TypeConverter:门面接口,内部会调用PropertyEditor和ConversionService

画张图来总结他们之间的关系

前面在举@Value的例子时说,类型转换是根据PropertyEditor来的,其实只说了一半,因为底层实际上是根据TypeConverter来转换的,所以@Value类型转换时也能使用ConversionService类转换,所以那张图实际上应该这么画才算对

2、Environment中到底是如何进行类型转换的?

这里我们回到开头提到的话题,Environment中到底是如何进行类型转换的,让我们看看Environment类的接口体系

Environment有个子接口ConfigurableEnvironment中,前面也提到过

它继承了ConfigurablePropertyResolver接口

而ConfigurablePropertyResolver有一个setConversionService方法

所以从这可以看出,Environment底层实际上是通过ConversionService实现类型转换的

这其实也就造成了一个问题,因为ConversionService和PropertyEditor属于并列关系,那么就会导致Environment无法使用PropertyEditor来进行类型转换,也就会丧失部分Spring提供的类型转换功能,就比如无法通过Environment将String转换成Resource对象,因为Spring没有实现String转换成Resource的Converter

当然你可以自己实现一个String转换成Resource的Converter,然后添加到ConversionService,之后Environment就支持String转换成Resource了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2982146.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Ubuntu+Systemd服务+实现开机自启

1.创建一个新的 systemd 服务文件 现在随便一个地方创建txt文档 如果想要启动sh脚本&#xff0c;就把下面的代码输入到txt文档中 [Unit] DescriptionRun Python script on specific executable run Afternetwork.target[Service] Typesimple ExecStart/home/tech/run_on_exe…

光接入网络的超宽带半导体光放大器

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 新颖的双有源层结构获得宽增益光谱&#xff0c;应用于多波单纤双向光放大 ----翻译Xiao Sun等人2016年撰写的文章&#xff0c;文中给出了宽光谱SOA的一种新颖的结构设计方法和仿真结果&#xff0c;但并未给…

社交媒体数据恢复:Instagram

Instagram数据恢复方法 在本文中&#xff0c;我们将详细介绍如何恢复在Instagram上删除的照片、消息和其他数据。请注意&#xff0c;这些方法可能适用于其他类型的社交媒体数据&#xff0c;但具体效果取决于数据的实际状态和存储设备的健康状况。 一、准备工作 在开始数据恢…

四川赢涟电子商务有限公司深耕抖音电商服务

在当今数字化、网络化高速发展的时代&#xff0c;电子商务行业异军突起&#xff0c;成为推动经济增长的重要力量。四川赢涟电子商务有限公司凭借其敏锐的市场洞察力和创新精神&#xff0c;专注于抖音电商服务&#xff0c;致力于为广大消费者提供便捷、高效、个性化的购物体验&a…

kafka启动报错(kafka.common.InconsistentClusterIdException)

文章目录 前言kafka启动报错(kafka.common.InconsistentClusterIdException)1. 查找日志2. 定位问题/解决 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不…

基于YOLOv8+Pyqt5火焰烟雾检测系统

1、YOLOv8的基本原理 YOLOv8是一种前沿的目标检测技术&#xff0c;它基于先前YOLO版本在目标检测任务上的成功&#xff0c;进一步提升了性能和灵活性。主要的创新点包括一个新的骨干网络、一个新的 Ancher-Free 检测头和一个新的损失函数&#xff0c;可以在从 CPU 到 GPU 的各…

Golang | Leetcode Golang题解之第47题全排列II

题目&#xff1a; 题解&#xff1a; func permuteUnique(nums []int) (ans [][]int) {sort.Ints(nums)n : len(nums)perm : []int{}vis : make([]bool, n)var backtrack func(int)backtrack func(idx int) {if idx n {ans append(ans, append([]int(nil), perm...))return}…

数据分析学习资源(未完)

1、PDF 数据分析自学攻略 增长黑客&#xff08;AARRR&#xff09; 量化思维

glib读写ini文件测试

函数简介 g_key_file_load_from_file g_key_file_load_from_file() 是 GLib 库中的一个函数&#xff0c;用于从指定的文件路径加载一个键值对文件&#xff08;通常是一个 .ini 风格的配置文件&#xff09;。这个函数是 GKeyFile 结构体相关API的一部分&#xff0c;GKeyFile 用…

C++面向对象程序设计 - 重载运算符进一步讨论

函数重载就是对一个已有的函数赋予新的含义&#xff0c;使之实现新的功能。因此一个函数名就可以用来代表不同功能的函数&#xff0c;也就是一名多用。运算符也可以重载&#xff0c;即运算符重载&#xff08;operator overloading&#xff09;。 一、非成员、非友元的重载运算…

STM32F103学习笔记 | 1.Keil5详细安装教程

Keil5详细安装教程 https://www.keil.com/download/product/

使用API有效率地管理Dynadot域名,自查账户信息

关于Dynadot Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮…

大型组织企业 怎么实现便捷高效的跨地区文件交换?

大型组织企业&#xff0c;尤其是银行、邮政、大型集团、跨国企业等&#xff0c;都会存在多个分支机构&#xff0c;会面临跨地区文件交换的场景和需求。 跨地区文件交换可能会遇到以下问题&#xff1a; 1、网络带宽限制&#xff1a;跨国或跨大陆传输时&#xff0c;网络带宽可能…

CC攻击频发,企业如何做好网络安全,该怎么防护能免遭CC攻击?

在当前网络现状下&#xff0c;随着信息技术的飞速发展&#xff0c;网络攻击手段也愈发多样化和复杂化。其中&#xff0c;CC攻击作为一种针对Web应用层的拒绝服务攻击&#xff0c;其危害日益凸显&#xff0c;对企业和个人造成了严重的威胁。下面我们就从多个角度详细分享关于CC攻…

LyricWikia, 一个让你玩物丧志的Python库

文章目录 LyricWikia: Python的歌词查找库背景LyricWikia是什么&#xff1f;安装简单的库函数使用方法场景示例搜索并显示歌词获取歌手的热门歌曲搜索并下载歌词 常见问题和解决方案总结 LyricWikia: Python的歌词查找库 背景 LyricWikia是一个用于查找和获取歌曲歌词的Python…

【项目实战】基于高并发服务器的搜索引擎

【项目实战】基于高并发服务器的搜索引擎 目录 【项目实战】基于高并发服务器的搜索引擎搜索引擎部分代码index.htmlindex.hpplog.hppparser.cc&#xff08;用于对网页的html文件切分且存储索引关系&#xff09;searcher.hpputil.hpphttp_server.cc&#xff08;用于启动服务器和…

【Linux的git操作】

Linux学习笔记---010 Linux的git操作1、什么是gitee2、git 准备工作2.1、查看是否安装了 git 版本工具2.2、安装 git 工具/更新成最新版本2.3、在gitee上创建远程仓库&#xff08;略&#xff09;2.4、提交file的初始化操作 3、git的“三板斧”3.1、add3.2、commit3.3、push3.4、…

Amazon云计算AWS之[2]弹性计算云EC2

文章目录 说明EC2基本架构Amazon机器映象&#xff08;AMI&#xff09;实例&#xff08;Instance&#xff09;弹性块存储&#xff08;EBS&#xff09; EC2关键技术地理区域和可用区域EC2通信机制弹性负载均衡监控服务自动缩放服务管理控制台 EC2安全及容错机制EC2弹性IP地址 说明…

Pyside6:多行按钮点击判断序号

在Pyside开发过程中会遇到这么个问题&#xff1a;当多个按钮在很多行中&#xff0c;需要在点击槽函数中确认按钮的行。 普通的按钮点击信号如下&#xff1a; clicked() 该信号并未有任何参数&#xff0c;无法得到有效的信息&#xff0c;那么如何完成点击哪个确定是哪个按钮呢…

计算机系列之校验码

6、校验码 1、码距 码距&#xff1a;就单个编码 A: 00 而言&#xff0c;其码距为 1&#xff0c;因为其只需要改变一位就变成另一个编码。**在两个编码中&#xff0c;从 A 码到 B 码转换所需要改变的位数成为码距&#xff0c;**如 A: 00 要转换为 B: 11&#xff0c;码距为2。一…