亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

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

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

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

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

1.從何說起

Spring 要從何說起呢?這個(gè)問題我考慮了很長時(shí)間。

因?yàn)?Spring 源碼太繁雜了,一定要選擇一個(gè)合適的切入點(diǎn),否則一上來就把各位小伙伴整懵了,那剩下的文章估計(jì)就不想看了。

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

2.簡單的案例

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

首先我們創(chuàng)建一個(gè)普通的 Maven 項(xiàng)目,引入 spring-beans 依賴:

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

然后我們創(chuàng)建一個(gè)實(shí)體類,再添加一個(gè)簡單的配置文件:

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

resources 目錄下創(chuàng)建配置文件:

<?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>

然后去加載這個(gè)配置文件:

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);
}

這里為了展示數(shù)據(jù)的讀取過程,我就先用這個(gè)已經(jīng)過期的 XmlBeanFactory 來加載,這并不影響我們閱讀源碼。

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

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

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

3.文件讀取

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

首先,Spring 中使用 Resource 接口來封裝底層資源,Resource 接口本身實(shí)現(xiàn)自 InputStreamSource 接口:

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

 

我們來看下這兩個(gè)接口的定義:

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

當(dāng)我們加載不同資源時(shí),對應(yīng)了 Resource 的不同實(shí)現(xiàn)類,來看下 Resource 的繼承關(guān)系:

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

 

可以看到,針對不同類型的數(shù)據(jù)源,都有各自的實(shí)現(xiàn),我們這里來重點(diǎn)看下 ClassPathResource 的實(shí)現(xiàn)方式。

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

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 的構(gòu)造方法有四個(gè),一個(gè)已經(jīng)過期的方法我這里沒有列出來。另外三個(gè),我們一般調(diào)用一個(gè)參數(shù)的即可,也就是傳入文件路徑即可,它內(nèi)部會調(diào)用另外一個(gè)重載的方法,給 classloader 賦上值(因?yàn)樵诤竺嬉ㄟ^ classloader 去讀取文件)。
  2. 在 ClassPathResource 初始化的過程中,會先調(diào)用 StringUtils.cleanPath 方法對傳入的路徑進(jìn)行清理,所謂的路徑清理,就是處理路徑中的相對地址、windows 系統(tǒng)下的 \ 變?yōu)?/ 等。
  3. getPath 方法用來返回文件路徑,這是一個(gè)相對路徑,不包含 classpath。
  4. resolveURL 方法表示返回資源的 URL,返回的時(shí)候優(yōu)先用 Class.getResource 加載,然后才會用 ClassLoader.getResource 加載,關(guān)于 Class.getResource 和 ClassLoader.getResource 的區(qū)別,又能寫一篇文章出來,我這里就大概說下,Class.getResource 最終還是會調(diào)用 ClassLoader.getResource,只不過 Class.getResource 會先對路徑進(jìn)行處理。
  5. getInputStream 讀取資源,并返回 InputStream 對象。
  6. createRelative 方法是根據(jù)當(dāng)前的資源,再創(chuàng)建一個(gè)相對資源。

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

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

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

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

在 Spring 框架,構(gòu)造出 Resource 對象之后,接下來還會把 Resource 對象轉(zhuǎn)為 EncodedResource,這里會對資源進(jìn)行編碼處理,編碼主要體現(xiàn)在 getReader 方法上,在獲取 Reader 對象時(shí),如果有編碼,則給出編碼格式:

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.小結(jié)

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

如果小伙伴們覺得有收獲,記得點(diǎn)個(gè)在看鼓勵下松哥哦~

分享到:
標(biāo)簽:Spring
用戶無頭像

網(wǎng)友整理

注冊時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

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

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定