javascript
Spring源码分析——资源访问利器Resource之接口和抽象类分析
從今天開始,一步步走上源碼分析的路。剛開始肯定要從簡單著手。我們先從Java發展史上最強大的框架——Spring、、、旗下的資源抽象接口Resource開始吧。
我看了好多分析Spring源碼的,每每一開始就是Spring IOC、AOP、BeanFactory這樣的Spring典型模塊,實在看厭了,這些暫且留到以后。我的想法是,分析就分析別人沒分析過的,或者以不同的角度來分析別人分析過的。
可能很多用了Spring多年的程序員對Resource都了解有限,畢竟訪問資源一般是搭建web工程框架的時候的事情。不過了解它也是非常有好處的。
這個接口的作用是可以讓我們更方便操縱底層資源。因為JDK操縱底層資源基本就是 java.net.URL 、java.io.File 、java.util.Properties這些。取資源基本是根據絕對路徑或當前類的相對路徑來取。從類路徑或Web容器上下文中獲取資源的時候也不方便。Resource接口提供了更強大的訪問底層資源的能力。
廢話不多說,看源碼之前先來看一下Resource的類結構。
一、類結構
一、Resource接口
如圖,Resouce接口并不是一個根接口,它繼承了一個簡單的父接口 InputStreamSource,這個接口只有一個方法,用以返回一個輸入流:
InputStream getInputStream() throws IOException;來,直接上Resource接口的源碼,中文是我根據英文注釋自己翻譯的,如下:
public interface Resource extends InputStreamSource {boolean exists(); // 資源是否存在boolean isReadable(); // 資源是否可讀boolean isOpen(); // 資源所代表的句柄是否被一個stream打開了 URL getURL() throws IOException; // 返回資源的URL的句柄 URI getURI() throws IOException; // 返回資源的URI的句柄 File getFile() throws IOException; // 返回資源的File的句柄long contentLength() throws IOException; // 資源內容的長度long lastModified() throws IOException; // 資源最后的修改時間 Resource createRelative(String relativePath) throws IOException; //根據資源的相對路徑創建新資源 String getFilename(); // 資源的文件名 String getDescription(); //資源的描述 }? 這個沒什么好說的,繼續!
?
二、抽象類AbstractResource
對于任何的接口而言,這個直接抽象類是重中之重,里面濃縮了接口的大部分公共實現。翻譯后如下:
public abstract class AbstractResource implements Resource {public boolean exists() { //判斷文件是否存在,若判斷過程產生異常(因為會調用SecurityManager來判斷),就關閉對應的流try {return getFile().exists();}catch (IOException ex) {try {InputStream is = getInputStream(); //getInputStream()方法會被子類重寫, is.close();return true;}catch (Throwable isEx) {return false; }}}public boolean isReadable() { // 直接返回true,可讀return true;}public boolean isOpen() { // 直接返回false,未被打開return false;}public URL getURL() throws IOException { // 留給子類重寫throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");}public URI getURI() throws IOException { //返回urlURL url = getURL();try {return ResourceUtils.toURI(url); //將url格式化后返回 }catch (URISyntaxException ex) {throw new NestedIOException("Invalid URI [" + url + "]", ex);}}public File getFile() throws IOException { // 留給子類重寫throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");}// 這個資源內容長度實際就是資源的字節長度,通過全部讀取一遍來判斷。這個方法調用起來很占資源啊!public long contentLength() throws IOException { InputStream is = this.getInputStream();Assert.state(is != null, "resource input stream must not be null"); //斷言try {long size = 0;byte[] buf = new byte[255];int read;while((read = is.read(buf)) != -1) {size += read;}return size;}finally {try {is.close();}catch (IOException ex) {}}}public long lastModified() throws IOException { // 返回資源的最后修改時間long lastModified = getFileForLastModifiedCheck().lastModified();if (lastModified == 0L) {throw new FileNotFoundException(getDescription() +" cannot be resolved in the file system for resolving its last-modified timestamp");}return lastModified;}// 這是Resource接口所沒有的方法,注釋的意思是“返回文件,給時間戳檢查”,要求子類重寫...protected File getFileForLastModifiedCheck() throws IOException {return getFile();}public Resource createRelative(String relativePath) throws IOException { // 留給子類重寫throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());}public String getFilename() { // 默認返回空(假設資源沒有文件名),除非子類重寫return null;}@Overridepublic String toString() { // toString返回文件描述return getDescription();}@Overridepublic boolean equals(Object obj) { // equals比較的就是2個資源描述是否一樣return (obj == this ||(obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));}@Overridepublic int hashCode() { // 返回資源描述的HashCodereturn getDescription().hashCode(); }}?
結論:
1、增加了一個方法,protected File getFileForLastModifiedCheck() throws IOException,要求子類實現,如果子類未實現,那么直接返回資源文件。這個方法的具體作用,后面再看實現類。
2、方法 contentLength() ,是一個很比較重量級的方法,它通過將資源全部讀取一遍來判斷資源的字節數。255字節的緩沖數組來讀取。子類一般會重寫。(調整一下緩沖數組的大小?)
3、getDescription() 是這個抽象類唯一沒有實現的接口方法,留給子類去實現,資源文件默認的equals()、hashCode() 都通過這個來判斷。
4、InputStreamSource這個祖先接口的唯一方法 getInputStream()也沒有被實現,留給子類。
?
三、Resource的子接口ContextResource和WritableResource
這兩個接口繼承于Resource,擁有Resource的全部方法。其中,ContextResource接口增加了一個方法:
String getPathWithinContext(); // 返回上下文內的路徑這個方法使得它的實現類有了返回當前上下文路徑的能力。
?
WritableResource接口增加了2個方法:
boolean isWritable(); // 是否可寫 OutputStream getOutputStream() throws IOException; //返回資源的寫入流這個方法使得它的實現類擁有了寫資源的能力。
?
四、重要的抽象類AbstractFileResolvingResource
這個抽象類繼承自AbstractResource,重寫了AbstractResource的大部分方法。
/** Copyright 2002-2011 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.core.io;import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.net.URLConnection;import org.springframework.util.ResourceUtils;/*** Abstract base class for resources which resolve URLs into File references,* such as {@link UrlResource} or {@link ClassPathResource}.** <p>Detects the "file" protocol as well as the JBoss "vfs" protocol in URLs,* resolving file system references accordingly.** @author Juergen Hoeller* @since 3.0*/ public abstract class AbstractFileResolvingResource extends AbstractResource {@Overridepublic File getFile() throws IOException { // 通過資源的URL得到資源本身,是文件就返回文件,否則返回描述URL url = getURL();if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(url).getFile();}return ResourceUtils.getFile(url, getDescription());}@Overrideprotected File getFileForLastModifiedCheck() throws IOException { //從<壓縮文件地址>中獲取文件URL url = getURL();if (ResourceUtils.isJarURL(url)) {URL actualUrl = ResourceUtils.extractJarFileURL(url);if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(actualUrl).getFile();}return ResourceUtils.getFile(actualUrl, "Jar URL");}else {return getFile();}}protected File getFile(URI uri) throws IOException { // 通過資源uri獲取文件if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {return VfsResourceDelegate.getResource(uri).getFile();}return ResourceUtils.getFile(uri, getDescription());}@Overridepublic boolean exists() { //判斷資源是否存在,如果是文件Url,直接獲取文件判斷,否則,建立連接來判斷。try {URL url = getURL();if (ResourceUtils.isFileURL(url)) {// Proceed with file system resolution...return getFile().exists();}else {// Try a URL connection content-length header...URLConnection con = url.openConnection();ResourceUtils.useCachesIfNecessary(con);HttpURLConnection httpCon =(con instanceof HttpURLConnection ? (HttpURLConnection) con : null);if (httpCon != null) {httpCon.setRequestMethod("HEAD");int code = httpCon.getResponseCode();if (code == HttpURLConnection.HTTP_OK) {return true;}else if (code == HttpURLConnection.HTTP_NOT_FOUND) {return false;}}if (con.getContentLength() >= 0) {return true;}if (httpCon != null) {// no HTTP OK status, and no content-length header: give up httpCon.disconnect();return false;}else {// Fall back to stream existence: can we open the stream?InputStream is = getInputStream();is.close();return true;}}}catch (IOException ex) {return false;}}@Overridepublic boolean isReadable() { // 是否可讀try {URL url = getURL();if (ResourceUtils.isFileURL(url)) {// Proceed with file system resolution...File file = getFile();return (file.canRead() && !file.isDirectory());}else {return true;}}catch (IOException ex) {return false;}}@Overridepublic long contentLength() throws IOException {URL url = getURL();if (ResourceUtils.isFileURL(url)) {// Proceed with file system resolution...return getFile().length();}else {// Try a URL connection content-length header...URLConnection con = url.openConnection();ResourceUtils.useCachesIfNecessary(con);if (con instanceof HttpURLConnection) {((HttpURLConnection) con).setRequestMethod("HEAD");}return con.getContentLength();}}@Overridepublic long lastModified() throws IOException {URL url = getURL();if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) {// Proceed with file system resolution...return super.lastModified();}else {// Try a URL connection last-modified header...URLConnection con = url.openConnection();ResourceUtils.useCachesIfNecessary(con);if (con instanceof HttpURLConnection) {((HttpURLConnection) con).setRequestMethod("HEAD");}return con.getLastModified();}}/*** Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime.*/private static class VfsResourceDelegate {public static Resource getResource(URL url) throws IOException {return new VfsResource(VfsUtils.getRoot(url));}public static Resource getResource(URI uri) throws IOException {return new VfsResource(VfsUtils.getRoot(uri));}}} View Code?
這個抽象類的子類都需要重寫繼承自AbstractResource的getURL()方法。因為絕大多數方法都依賴這個方法,進行資源在url上的操作。
所以在查看資源情況的時候,需要根據url建立連接來查看。
?
?
? PS:框架感覺大都是這樣,不難,設計模式也運用的不多。卻有一種大巧不工、重劍無鋒的感覺,因為代碼運用真的非常簡練。
分析源碼,很大程度上也鍛煉了自己的英文文檔閱讀能力。共勉。
?
?
轉載于:https://www.cnblogs.com/zrtqsk/p/4015323.html
總結
以上是生活随笔為你收集整理的Spring源码分析——资源访问利器Resource之接口和抽象类分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 组织配置java项目的外部lib包
- 下一篇: AngularJS(6)-选择框Sele