曹工談Spring Boot:Spring boot中怎麼進行外部化配置,一不留神摔一跤;一路debug,原來是我太年輕了
Will return a {@link UrlResource} if the location value is a URL,
> * and a {@link ClassPathResource} if it is a non-URL path or a
> * "classpath:" pseudo-URL.
> ```
大體翻譯:
> ResourceLoader介面的預設實現,被ResourceEditor使用,同時,是AbstractApplicationContext的基類。
>
> 也能被單獨使用。
>
> 當傳入的value,是一個URL,則封裝為一個UrlResource並返回;
>
> 當傳入的是一個非URL,或者是一個類似於"classpath:"這樣的,則返回一個ClassPathResource
對其的介紹到此打住。繼續前面的程式碼:
```java
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
...
// 1
if (location.startsWith("/")) {
return getResourceByPath(location);
} // 2
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 3 Try to parse the location as a URL...
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
```
* 1,判斷是否以 `/`開頭
* 2,判斷是否以classpath開頭
* 3,作為引數,看看能不能 被解析為一個URL
然後3處這裡,URL,是 jdk 的核心類,裡面 debug 進去挺深的,直接執行完這一句之後,我們看看url這個引數的值:
![](https://img2020.cnblogs.com/blog/519126/202005/519126-20200520153922495-2033478849.png)
總的來說,這裡就是:你給一個字串,URL按照它的格式,來解析為各個欄位:比如,協議,host,port,query等等。但是,不代表這個URL就是可以訪問的,如果是file,不代表這個檔案就存在。這裡只是按照URL的格式去解析而已。
我們繼續下一句:
```java
URL url = new URL(location);
// 1
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
```
這裡1處,判斷是否為file,如果是,則會new一個FileUrlResource。
該類的類結構如下:
![](https://img2020.cnblogs.com/blog/519126/202005/519126-20200520154430214-152385115.png)
前面呼叫了new ,我們看看:
```java
public FileUrlResource(URL url) {
super(url);
}
```
呼叫了父類:
```java
/**
* Original URI, if available; used for URI and File access.
*/
@Nullable
private final URI uri;
/**
* Original URL, used for actual access.
*/
private final URL url;
/**
* Cleaned URL (with normalized path), used for comparisons.
*/
private final URL cleanedUrl;
public UrlResource(URL url) {
this.url = url;
this.cleanedUrl = getCleanedUrl(this.url, url.toString());
this.uri = null;
}
```
總的來說,就是利用你傳入的URL,進行clean,然後儲存到了cleanedUrl。
我們這裡,經過clean後,
* clean後,cleanedUrl的值為:`file:config/application.properties`
* 原始的:`file:./config/application.properties`
差別不大,主要是去掉了開頭的`./`。
至此,我們的`FileUrlResource`就構造結束了,至此,我們完成了下面這行的解析。
```java
Resource resource = this.resourceLoader.getResource(location);
```
## 判斷resource是否存在
前面我們看到,FileUrlResource,繼承了`org.springframework.core.io.AbstractFileResolvingResource`介面。
而我們這裡呼叫:
```java
resource.exists()
```
就會進入其父類的exists方法
```java
org.springframework.core.io.AbstractFileResolvingResource#exists
public boolean exists() {
try {
// 1
URL url = getURL();
if (ResourceUtils.isFileURL(url)) {
//2 Proceed with file system resolution
return getFile().exists();
}
...
}
```
* 1,獲取url
* 2,獲取file
* 3,判斷是否存在。
其中2處,繼續:
```java
@Override
public File getFile() throws IOException {
// 1
File file = this.file;
if (file != null) {
return file;
}
// 2
file = super.getFile();
// 3
this.file = file;
return file;
}
```
* 1,查詢本地是否快取
* 2,沒快取,則呼叫super類的getFile去獲取
* 3,快取。
繼續進入2處,
```java
org.springframework.core.io.UrlResource#getFile
public File getFile() throws IOException {
// 1
if (this.uri != null) {
return super.getFile(this.uri);
}
else {
// 2
return super.getFile();
}
}
```
* 1,我們這裡uri是null,會進入2處
* 2,呼叫父類。
```java
org.springframework.core.io.AbstractFileResolvingResource#getFile()
@Override
public File getFile() throws IOException {
// 1
URL url = getURL();
// 2
return ResourceUtils.getFile(url, getDescription());
}
```
* 1處,獲取url
* 2處,獲取file。
繼續進入2處:
```java
org.springframework.util.ResourceUtils#getFile(java.net.URL, java.lang.String)
public static File getFile(URL resourceUrl, String description) throws FileNotFoundException {
try {
// 1
return new File(toURI(resourceUrl).getSchemeSpecificPart());
}
catch (URISyntaxException ex) {
// Fallback for URLs that are not valid URIs (should hardly ever happen).
return new File(resourceUrl.getFile());
}
}
```
這裡,傳入的resourceURL,型別為URL, 在idea中顯示為:
```java
file:./config/application.properties
```
toURI,大家可以大致看下,
```java
org.springframework.util.ResourceUtils#toURI(java.net.URL)
public static URI toURI(URL url) throws URISyntaxException {
return toURI(url.toString());
}
```
```java
public static URI toURI(String location) throws URISyntaxException {
return new URI(StringUtils.replace(location, " ", "%20"));
}
```
上面幹了啥,就是把路徑裡的" "換成了"%20"。然後new了一個URI。
## URI、URL的差別簡述
這兩個東西,太學術了,簡單理解,就是URI,指代的東西更多,包含的範圍更廣,URI表示中國的話,URL可能只能表示臺灣省。(我他麼一顆紅心)
總的來說,uri 不一定可以訪問,url基本是可以的。
![](https://img2020.cnblogs.com/blog/519126/202005/519126-20200520160618133-391325442.png)
參考: