上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0

          Java中,將不同來源的資源抽象成URL,通過注冊不同的handlerURLStreamHandler)來處理不同來源的資源的讀取邏輯,一般handler的類型使用不同前綴(協議,protocol)來識別,如“file:”、“http:”、“jar:”等,然而URL沒有默認定義相對classpathServletContext等資源的handler,雖然可以注冊自己的URLStreamHandler來解析特定的URL前綴(協議),比如“classpath:”,然而這需要了解URL的實現機制,而且URL也沒有提供一些基本的方法,如檢查當前資源是否存在、檢查當前資源是否可讀等方法。因而Spring對其內部使用到的資源實現了自己的抽象結構:Resource接口來封裝底層資源:

          public interface InputStreamSource {

              InputStream getInputStream() throws IOException;

          }

          public interface Resource extends InputStreamSource {

              boolean exists();

              boolean isReadable();

              boolean isOpen();

              URL getURL() throws IOException;

              URI getURI() throws IOException;

              File getFile() throws IOException;

              long lastModified() throws IOException;

              Resource createRelative(String relativePath) throws IOException;

              String getFilename();

              String getDescription();

          }

          InputStreamSource封裝任何能返回InputStream的類,比如Fileclasspath下的資源、Byte Array等。它只有一個方法定義:getInputStream(),該方法返回一個新的InputStream對象。

          Resource接口抽象了所有Spring內部使用到的底層資源:FileURLclasspath等。首先,它定義了三個判斷當前資源狀態的方法:存在性(exists)、可讀性(isReadable)、是否處于打開狀態(isOpen)。在C語言中,當我們拿到一個文件句柄時,我們要調用open方法打開文件才可以真正讀取該文件,但是在Java中并沒有顯示的定義open方法,一般當我們創建一個InputStreamReader時,該資源(文件)就已經處于打開狀態了,因而這里的isOpen方法并不是判斷當前資源是否已經處于打開的可操作狀態,這里是表示Resource接口所抽象的底層資源是否可以多次調用getInputStream()方法,如果該方法返回true,則不可以多次調用getInputStream()方法。在Spring 2.5.6的實現中,只有InputStreamResource類的isOpen()方法返回true,其余都返回false

          另外,Resource接口還提供了不同資源到URLURIFile類型的轉換,以及獲取lastModified屬性、文件名(不帶路徑信息的文件名,getFilename())的方法。為了便于操作,Resource還提供了基于當前資源創建一個相對資源的方法:createRelative();在錯誤處理中需要詳細的打印出錯的資源文件,因而Resource還提供了getDescription()方法用于在錯誤處理中的打印信息。

          Spring 2.5.6中,所有實現Resource的接口類繼承關系圖如下:

          即對不同來源的資源文件都有相應的Resource實現:文件(FileSystemResource)、classpath資源(ClassPathResource)、URL資源(UrlResource)、InputStream資源(InputStreamResource)、Byte數組(ByteArrayResource)等。

          AbstractResource

          AbstractResource是對Resource的基本實現,所有Resource實現類都繼承了該類,所有繼承該類的Resource一般只需要實現以下方法即可:

          public File getFile() throws IOException

          public URL getURL() throws IOException

          public String getDescription()

          public InputStream getInputStream() throws IOException

          該類默認實現中,將toStringequalshashCode都代理給Description屬性;isReadable總是返回true,而isOpen總是返回falseexists方法實現中,先調用getFile返回的File對象的exists方法,如果失敗,查看是否可以獲得InputStream,如果可以,返回true,否則,返回falsegetURLgetFilecreateRelative方法拋出FileNotFoundException,而getURI則代理給getURL方法。

          ByteArrayResource

          ByteArrayResource是一個簡單的Resource實現,它是對二進制數組的封裝,每次調用getInputStream時都會以這個二進制數組作為源創建一個ByteArrayInputStream。它的exists方法總是返回true,而且重寫了equalshashCode的方法,以判斷二進制數組的內容;它的description屬性可以是用戶自定義,也可以使用默認值:resource loaded from byte array

          public final byte[] getByteArray() {

              return this.byteArray;

          }

          public boolean exists() {

              return true;

          }

          public InputStream getInputStream() throws IOException {

              return new ByteArrayInputStream(this.byteArray);

          }

          FileSystemResource

          FileSystemResource是對File的封裝,在構建FileSystemResource時可以傳入File對象或路徑字符串(這里的路徑可以是相對路徑,相對路徑是相對于System.getProperty(“user.dir”)的值所在的路徑,也可以是絕對路徑,也可以是“file:”開頭的路徑值),在內部會創建相應的File對象,并且計算其path值,這里的path是計算完“.”和“..”影響的值(規格化)。

          getInputStream方法中,使用該File對象創建FileInputStream;而path值作為description屬性、equalshashCode等方法的實現;所有其他方法(existsisReadablegetURL等)都代理給File對象;createRelative方法中使用path計算相對路徑,其算法是:找到最后一個路徑分隔符(/),將相對路徑添加到該分隔符之后,傳入的相對路徑可以是以路徑分割符(/)開頭,也可以不以分隔符(/)開頭,他們的效果是一樣的,對相對路徑存在的“.”和“..”會在創建FileSystemResource類時處理。最后,當使用將一個目錄的File對象構建FileSystemResource時,調用createRelative方法,其相對路徑的父目錄和當前FileSystemResource的父目錄相同,比如使用”/home/Levin/dir1”目錄創建FileSystemResource對象,該Resource對象調用createRelative,并傳入”file”,那么出現的結果為”/home/Levin/file”,如果要得到”/home/Levin/dir1/file”,那么構建FileSystemResource時,應該傳入”/home/Levin/dir1/”字符串。

          public boolean isReadable() {

              return (this.file.canRead() && !this.file.isDirectory());

          }

          public InputStream getInputStream() throws IOException {

              return new FileInputStream(this.file);

          }

          public Resource createRelative(String relativePath) {

              String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);

              return new FileSystemResource(pathToUse);

          }

          public String getDescription() {

              return "file [" + this.file.getAbsolutePath() + "]";

          }

          UrlResource

          UrlResource是對URLURI的封裝。在構建UrlResource時可以傳入URLURIPath字符串(帶協議字符串,如”file:”)。在UrlResource內部還會創建一個cleanedUrl,它是規格化(計算“.”和“..”后的值),該URL將會用于equalshashCode方法的實現。

          getInputStream方法實現中,它使用URL.openConnection()方法獲取URLConnection,后調用該URLConnectiongetInputStream方法。對getFile()方法,只支持文件系統的資源,即URL字符串的協議部分為”file:”UrlResource還支持從jarzipvfszipwsjar等內部文件,以jar為例,這些文件的字符串表達為:jar:file:/<jarpath>/jarfile.jar!/<filepath>/filename,如jar:file:/E:/Program%20Files/eclipse-juno/plugins/org.junit_4.10.0.v4_10_0_v20120426-0900/junit.jar!/org/junit/Test.class,然而這些內部文件本身并沒有lastModified的屬性,因而對這些內部文件,UrlResourcejarzip等文件的lastModified視為這些內部文件的lastModified屬性。對createRelative方法,直接使用URL提供的構造函數,忽略傳入的relativePath中的路徑分隔符“/”。

          public InputStream getInputStream() throws IOException {

              URLConnection con = this.url.openConnection();

              con.setUseCaches(false);

              return con.getInputStream();

          }

          protected File getFileForLastModifiedCheck() throws IOException {

              if (ResourceUtils.isJarURL(this.url)) {

                  URL actualUrl = ResourceUtils.extractJarFileURL(this.url);

                  return ResourceUtils.getFile(actualUrl);

              }

              else {

                  return getFile();

              }

          }

          public Resource createRelative(String relativePath) throws MalformedURLException {

              if (relativePath.startsWith("/")) {

                  relativePath = relativePath.substring(1);

              }

              return new UrlResource(new URL(this.url, relativePath));

          }

          ClassPathResource

          classpath下資源的封裝,或者說是對ClassLoader.getResource()方法或Class.getResource()方法的封裝。它支持在當前classpath中讀取資源文件。可以傳入相對classpath的文件全路徑名和ClassLoader構建ClassPathResource,或忽略ClassLoader采用默認ClassLoader(即Thread Context ClassLoader),此時在getInputStream()方法實現時時會使用ClassLoader.getResourceAsStream()方法,由于使用ClassLoader獲取資源時默認相對于classpath的根目錄,因而構造函數會忽略開頭的“/”字符。ClassPathResource還可以使用文件路徑和Class作為參數構建,此時若文件路徑以“/”開頭,表示該文件為相對于classpath的絕對路徑,否則為相對Class實例的相對路徑,在getInputStream()方法實現時使用Class.getResourceAsStream()方法。

          getFile()方法只支持存在于文件系統中的資源;對lastModified的屬性,若是jarzip等文件中的資源,則采用jarzip文件本身的lastModified屬性;equals會同時判斷pathclassloaderclazz字段,而hashCode則只使用path

          public InputStream getInputStream() throws IOException {

              InputStream is = null;

              if (this.clazz != null) {

                  is = this.clazz.getResourceAsStream(this.path);

              }

              else {

                  is = this.classLoader.getResourceAsStream(this.path);

              }

              if (is == null) {

                  throw new FileNotFoundException(

                          getDescription() + " cannot be opened because it does not exist");

              }

              return is;

          }

          public URL getURL() throws IOException {

              URL url = null;

              if (this.clazz != null) {

                  url = this.clazz.getResource(this.path);

              }

              else {

                  url = this.classLoader.getResource(this.path);

              }

              if (url == null) {

                  throw new FileNotFoundException(

                          getDescription() + " cannot be resolved to URL because it does not exist");

              }

              return url;

          }

          public File getFile() throws IOException {

              return ResourceUtils.getFile(getURL(), getDescription());

          }

          protected File getFileForLastModifiedCheck() throws IOException {

              URL url = getURL();

              if (ResourceUtils.isJarURL(url)) {

                  URL actualUrl = ResourceUtils.extractJarFileURL(url);

                  return ResourceUtils.getFile(actualUrl);

              }

              else {

                  return ResourceUtils.getFile(url, getDescription());

              }

          }

          public Resource createRelative(String relativePath) {

              String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);

              return new ClassPathResource(pathToUse, this.classLoader, this.clazz);

          }

          InputStreamResource

          InputStreamResource是對InputStream的封裝,它接收InputStream作為構造函數參數,它的isOpen總是返回true,并且只能被讀取一次(即getInputStream方法只能被調用一次),existsisReadable方法也總是返回true。由于它不能被多次讀取,只有當不用多次讀取的時候才使用該類,并且只有當沒有其他可用Resource類時才使用該類。在Spring內部貌似沒有使用它。它只實現了getInputStream方法:

          public InputStream getInputStream() throws IOException, IllegalStateException {

              if (this.read) {

                  throw new IllegalStateException("InputStream has already been read - " +

                          "do not use InputStreamResource if a stream needs to be read multiple times");

              }

              this.read = true;

              return this.inputStream;

          }

          DescriptiveResource

          DescriptiveResource是對非物理資源的Description的封裝。它實現了getDescription()方法。ResourceDescription屬性主要用于錯誤處理時能更加準確的打印出錯位置的信息,DescriptiveResource提供對那些需要提供Resource接口中的Description屬性作為錯誤打印信息的方法自定義的描述信息。比如在BeanDefinitionReader中,在僅僅使用InputSource作為源加載BeanDefinition時,就可以使用DescriptiveResource定義自己的Description,從而在出錯信息中可以方便的知道問題源在哪里。

          BeanDefinitionResource

          SpringResource可以用于非物理資源的抽,BeanDefinitionResource是對BeanDefinition的封裝。BeanDefinitionResource類似DescriptiveResource,它也只實現了getDescription()方法,用于在解析某個BeanDefinition出錯時顯示錯誤源信息:

          public String getDescription() {

              return "BeanDefinition defined in " + this.beanDefinition.getResourceDescription();

          }

          ContextResource接口

          Spring中還定義了ContextResource接口,繼承自Resource接口,只包含一個方法:

          public interface ContextResource extends Resource {

              String getPathWithinContext();

          }

          getPathWithContext()方法相對于Context的路徑,如ServletContextPortletContextclasspathFileSystem等,在Spring core中它有兩個實現類FileSystemContextResourceClassPathContextResource,他們分別是FileSystemResourceLoaderDefaultResourceLoader中的內部類,他們對getPathWithContext()方法的實現只是簡單的返回path值。

          另外,在Spring Web模塊中,有一個ServletContextResource實現類,它使用ServletContextpath作為參數構造,getInputStreamgetURLgetURIgetFile等方法中將實現代理給ServletContext,其中getPathWithContext方法依然返回path字符串:

          public boolean exists() {

              try {

                  URL url = this.servletContext.getResource(this.path);

                  return (url != null);

              }

              catch (MalformedURLException ex) {

                  return false;

              }

          }

          public InputStream getInputStream() throws IOException {

              InputStream is = this.servletContext.getResourceAsStream(this.path);

              if (is == null) {

                  throw new FileNotFoundException("Could not open " + getDescription());

              }

              return is;

          }

          public URL getURL() throws IOException {

              URL url = this.servletContext.getResource(this.path);

              if (url == null) {

                  throw new FileNotFoundException(

                          getDescription() + " cannot be resolved to URL because it does not exist");

              }

              return url;

          }

          public File getFile() throws IOException {

              String realPath = WebUtils.getRealPath(this.servletContext, this.path);

              return new File(realPath);

          }

          public Resource createRelative(String relativePath) {

              String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);

              return new ServletContextResource(this.servletContext, pathToUse);

          }

          posted on 2012-12-01 12:51 DLevin 閱讀(6755) 評論(1)  編輯  收藏 所屬分類: Spring

          FeedBack:
          # re: 深入Spring IOC源碼之Resource
          2014-04-14 18:14 | luchy
          你好,我也有閱讀源碼的習慣,覺得你寫的不錯  回復  更多評論
            

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 治县。| 宁都县| 上高县| 定西市| 德庆县| 和林格尔县| 江陵县| 子洲县| 耒阳市| 永兴县| 比如县| 宾川县| 佛学| 天祝| 定兴县| 富川| 北川| 永福县| 仙居县| 台东市| 大关县| 泸州市| 武宁县| 靖江市| 高邑县| 册亨县| 南平市| 五家渠市| 博野县| 湘乡市| 龙胜| 拜泉县| 平武县| 格尔木市| 土默特右旗| 渝中区| 江华| 巴马| 广西| 攀枝花市| 宽城|