日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

上周把話撂出來,看起來小伙伴們都挺期待的,其實松哥也迫不及待想要開啟一個全新的系列。

但是目前的 Spring Security 系列還在連載中,還沒寫完。連載這事,一鼓作氣,再而衰三而竭,一定要一次搞定,Spring Security 如果這次放下來,以后就很難再拾起來了。

所以目前的更新還是 Spring Security 為主,同時 Spring 源碼解讀每周至少更新一篇,等 Spring Security 系列更新完畢后,就開足馬力更新 Spring 源碼。其實 Spring Security 中也有很多和 Spring 相通的地方,Spring Security 大家文章認真看,松哥不會讓大家失望的!

1.從何說起

Spring 要從何說起呢?這個問題我考慮了很長時間。

因為 Spring 源碼太繁雜了,一定要選擇一個合適的切入點,否則一上來就把各位小伙伴整懵了,那剩下的文章估計就不想看了。

想了很久之后,我決定就先從配置文件加載講起,在逐步展開,配置文件加載也是我們在使用 Spring 時遇到的第一個問題,今天就先來說說這個話題。

2.簡單的案例

先來一個簡單的案例,大家感受一下,然后我們順著案例講起。

首先我們創建一個普通的 Maven 項目,引入 spring-beans 依賴:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>

然后我們創建一個實體類,再添加一個簡單的配置文件:

public class User {
    private String username;
    private String address;
    //省略 getter/setter
}

resources 目錄下創建配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.JAVAboy.loadxml.User" id="user"/>
</beans>

然后去加載這個配置文件:

public static void main(String[] args) {
    XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
    User user = factory.getBean(User.class);
    System.out.println("user = " + user);
}

這里為了展示數據的讀取過程,我就先用這個已經過期的 XmlBeanFactory 來加載,這并不影響我們閱讀源碼。

上面這個是一個非常簡單的 Spring 入門案例,相信很多小伙伴在第一次接觸 Spring 的時候,寫出來的可能都是這個 Demo。

在上面這段代碼執行過程中,首先要做的事情就是先把 XML 配置文件加載到內存中,再去解析它,再去。。。。。

一步一步來吧,先來看 XML 文件如何被加入到內存中去。

3.文件讀取

文件讀取在 Spring 中很常見,也算是一個比較基本的功能,而且 Spring 提供的文件加載方式,不僅僅在 Spring 框架中可以使用,我們在項目中有其他文件加載需求也可以使用。

首先,Spring 中使用 Resource 接口來封裝底層資源,Resource 接口本身實現自 InputStreamSource 接口:

Spring 源碼第一篇開整!配置文件是怎么加載的?

 

我們來看下這兩個接口的定義:

public interface InputStreamSource {
 InputStream getInputStream() throws IOException;
}
public interface Resource extends InputStreamSource {
 boolean exists();
 default boolean isReadable() {
  return exists();
 }
 default boolean isOpen() {
  return false;
 }
 default boolean isFile() {
  return false;
 }
 URL getURL() throws IOException;
 URI getURI() throws IOException;
 File getFile() throws IOException;
 default ReadableByteChannel readableChannel() throws IOException {
  return Channels.newChannel(getInputStream());
 }
 long contentLength() throws IOException;
 long lastModified() throws IOException;
 Resource createRelative(String relativePath) throws IOException;
 @Nullable
 String getFilename();
 String getDescription();

}

代碼倒不難,我來稍微解釋下:

  1. InputStreamSource 類只提供了一個 getInputStream 方法,該方法返回一個 InputStream,也就是說,InputStreamSource 會將傳入的 File 等資源,封裝成一個 InputStream 再重新返回。
  2. Resource 接口實現了 InputStreamSource 接口,并且封裝了 Spring 內部可能會用到的底層資源,如 File、URL 以及 classpath 等。
  3. exists 方法用來判斷資源是否存在。
  4. isReadable 方法用來判斷資源是否可讀。
  5. isOpen 方法用來判斷資源是否打開。
  6. isFile 方法用來判斷資源是否是一個文件。
  7. getURL/getURI/getFile/readableChannel 分別表示獲取資源對應的 URL/URI/File 以及將資源轉為 ReadableByteChannel 通道。
  8. contentLength 表示獲取資源的大小。
  9. lastModified 表示獲取資源的最后修改時間。
  10. createRelative 表示根據當前資源創建一個相對資源。
  11. getFilename 表示獲取文件名。
  12. getDescription 表示在資源出錯時,詳細打印出出錯的文件。

當我們加載不同資源時,對應了 Resource 的不同實現類,來看下 Resource 的繼承關系:

Spring 源碼第一篇開整!配置文件是怎么加載的?

 

可以看到,針對不同類型的數據源,都有各自的實現,我們這里來重點看下 ClassPathResource 的實現方式。

ClassPathResource 源碼比較長,我這里挑一些關鍵部分來和大家分享:

public class ClassPathResource extends AbstractFileResolvingResource {

 private final String path;

 @Nullable
 private ClassLoader classLoader;

 @Nullable
 private Class<?> clazz;

 public ClassPathResource(String path) {
  this(path, (ClassLoader) null);
 }
 public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
  Assert.notNull(path, "Path must not be null");
  String pathToUse = StringUtils.cleanPath(path);
  if (pathToUse.startsWith("/")) {
   pathToUse = pathToUse.substring(1);
  }
  this.path = pathToUse;
  this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
 }
 public ClassPathResource(String path, @Nullable Class<?> clazz) {
  Assert.notNull(path, "Path must not be null");
  this.path = StringUtils.cleanPath(path);
  this.clazz = clazz;
 }
 public final String getPath() {
  return this.path;
 }
 @Nullable
 public final ClassLoader getClassLoader() {
  return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader);
 }
 @Override
 public boolean exists() {
  return (resolveURL() != null);
 }
 @Nullable
 protected URL resolveURL() {
  if (this.clazz != null) {
   return this.clazz.getResource(this.path);
  }
  else if (this.classLoader != null) {
   return this.classLoader.getResource(this.path);
  }
  else {
   return ClassLoader.getSystemResource(this.path);
  }
 }
 @Override
 public InputStream getInputStream() throws IOException {
  InputStream is;
  if (this.clazz != null) {
   is = this.clazz.getResourceAsStream(this.path);
  }
  else if (this.classLoader != null) {
   is = this.classLoader.getResourceAsStream(this.path);
  }
  else {
   is = ClassLoader.getSystemResourceAsStream(this.path);
  }
  if (is == null) {
   throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
  }
  return is;
 }
 @Override
 public URL getURL() throws IOException {
  URL url = resolveURL();
  if (url == null) {
   throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
  }
  return url;
 }
 @Override
 public Resource createRelative(String relativePath) {
  String pathToUse = StringUtils.ApplyRelativePath(this.path, relativePath);
  return (this.clazz != null ? new ClassPathResource(pathToUse, this.clazz) :
    new ClassPathResource(pathToUse, this.classLoader));
 }
 @Override
 @Nullable
 public String getFilename() {
  return StringUtils.getFilename(this.path);
 }
 @Override
 public String getDescription() {
  StringBuilder builder = new StringBuilder("class path resource [");
  String pathToUse = this.path;
  if (this.clazz != null && !pathToUse.startsWith("/")) {
   builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
   builder.append('/');
  }
  if (pathToUse.startsWith("/")) {
   pathToUse = pathToUse.substring(1);
  }
  builder.append(pathToUse);
  builder.append(']');
  return builder.toString();
 }
}
  1. 首先,ClassPathResource 的構造方法有四個,一個已經過期的方法我這里沒有列出來。另外三個,我們一般調用一個參數的即可,也就是傳入文件路徑即可,它內部會調用另外一個重載的方法,給 classloader 賦上值(因為在后面要通過 classloader 去讀取文件)。
  2. 在 ClassPathResource 初始化的過程中,會先調用 StringUtils.cleanPath 方法對傳入的路徑進行清理,所謂的路徑清理,就是處理路徑中的相對地址、windows 系統下的 \ 變為 / 等。
  3. getPath 方法用來返回文件路徑,這是一個相對路徑,不包含 classpath。
  4. resolveURL 方法表示返回資源的 URL,返回的時候優先用 Class.getResource 加載,然后才會用 ClassLoader.getResource 加載,關于 Class.getResource 和 ClassLoader.getResource 的區別,又能寫一篇文章出來,我這里就大概說下,Class.getResource 最終還是會調用 ClassLoader.getResource,只不過 Class.getResource 會先對路徑進行處理。
  5. getInputStream 讀取資源,并返回 InputStream 對象。
  6. createRelative 方法是根據當前的資源,再創建一個相對資源。

這是 ClassPathResource,另外一個大家可能會接觸到的 FileSystemResource ,小伙伴們可以自行查看其源碼,比 ClassPathResource 簡單。

如果不是使用 Spring,我們僅僅想自己加載 resources 目錄下的資源,也可以采用這種方式:

ClassPathResource resource = new ClassPathResource("beans.xml");
InputStream inputStream = resource.getInputStream();

拿到 IO 流之后自行解析即可。

在 Spring 框架,構造出 Resource 對象之后,接下來還會把 Resource 對象轉為 EncodedResource,這里會對資源進行編碼處理,編碼主要體現在 getReader 方法上,在獲取 Reader 對象時,如果有編碼,則給出編碼格式:

public Reader getReader() throws IOException {
 if (this.charset != null) {
  return new InputStreamReader(this.resource.getInputStream(), this.charset);
 }
 else if (this.encoding != null) {
  return new InputStreamReader(this.resource.getInputStream(), this.encoding);
 }
 else {
  return new InputStreamReader(this.resource.getInputStream());
 }
}

所有這一切搞定之后,接下來就是通過 XmlBeanDefinitionReader 去加載 Resource 了。

4.小結

好啦,今天主要和小伙伴們分享一下 Spring 中的資源加載問題,這是容器啟動的起點,下篇文章我們來看 XML 文件的解析。

如果小伙伴們覺得有收獲,記得點個在看鼓勵下松哥哦~

分享到:
標簽:Spring
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定