ClassPathResource详解

ClassPathResource详解
ClassPathReource resource=new ClassPathResource("spring_beans.xml");
1: public class ClassPathResource extends AbstractFileResolvingResource
      在ClassPathResource中,含参数String path的构造函数:
     
     public ClassPathResource(String path ) {
         this ( path , (ClassLoader) null);
    }
 2:上述构造函数指向了另外一个构造函数:
          
     public ClassPathResource (String path , 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());
    }
 能够看到path由StringUtils的cleanPath方法返回了pathToUse。由此,我们找到StringUtils的cleanPath方法
3: public abstract class StringUtils
  
public static String cleanPath (String path ) {
         if ( path == null) {
             return null ;
        }
        String pathToUse = replace( path , WINDOWS_FOLDER_SEPARATOR , FOLDER_SEPARATOR);
         int prefixIndex = pathToUse .indexOf( ":" );
        String prefix = "" ;
         if ( prefixIndex != -1) {
             prefix = pathToUse .substring(0, prefixIndex + 1);
             pathToUse = pathToUse .substring( prefixIndex + 1);
        }
         if ( pathToUse .startsWith( FOLDER_SEPARATOR)) {
             prefix = prefix + FOLDER_SEPARATOR;
             pathToUse = pathToUse .substring(1);
        }
 
        String[] pathArray = delimitedListToStringArray( pathToUse, FOLDER_SEPARATOR );
        List<String> pathElements = new LinkedList<String>();
         int tops = 0;
 
         for ( int i = pathArray. length - 1; i >= 0; i --) {
            String element = pathArray [ i ];
             if ( CURRENT_PATH .equals( element)) {
                 // Points to current directory - drop it.
            }
             else if ( TOP_PATH.equals( element)) {
                 // Registering top path found.
                 tops ++;
            }
             else {
                 if ( tops > 0) {
                     // Merging path element with element corresponding to top path.
                     tops --;
                }
                 else {
                     // Normal path element found.
                     pathElements .add(0, element );
                }
            }
        }
 
         // Remaining top paths need to be retained.
         for ( int i = 0; i < tops; i++) {
             pathElements .add(0, TOP_PATH);
        }
 
         return prefix + collectionToDelimitedString( pathElements, FOLDER_SEPARATOR );
    }
     4:StringUtils类中 replace方法
public static String replace (String inString , String oldPattern , String newPattern ) {
         if (! hasLength( inString ) || ! hasLength( oldPattern) || newPattern == null ) {
             return inString ;
        }
        StringBuilder sb = new StringBuilder();
         int pos = 0; // our position in the old string
         int index = inString .indexOf( oldPattern );
         // the index of an occurrence we've found, or -1
         int patLen = oldPattern.length();
         while ( index >= 0) {
             sb.append( inString .substring( pos , index ));
             sb.append( newPattern );
             pos = index + patLen;
             index = inString .indexOf( oldPattern, pos );
        }
         sb.append( inString .substring( pos ));
         // remember to append any characters to the right of a match
         return sb .toString();
    }
     5:StringUtils类类中的hasLength方法。由此可以看出,同样的方法名,不同的方法签名,然后在其中一个方法中引用另外一个方法。好多类都是这么用的,就像开始的时候的构造函数那样,虽然不知道好处 是什么,但先记下来。
     public static boolean hasLength (String str ) {
         return hasLength((CharSequence) str );
    }
     public static boolean hasLength (CharSequence str ) {
         return ( str != null && str .length() > 0);
    }
     跟踪到这里,可以知道hasLength方法的目的就是str不为空且str的长度大于0。突然发现CharSequence这个类没接触过,来看一下它的源码
  6: public interface  CharSequence{}
  额,源码没看懂 就不粘贴过来了。
    7:回到StringUtils的replace方法
      首先判断传入的三个参数,如果为空后者长度小于0,直接返回inString;那么我看一下这三个参数都是什么:
     inString :path 这个就是我们传入的文件名
     oldPattern: private static final String WINDOWS_FOLDER_SEPARATOR = "\\" ;
     newPattern: private static final String FOLDER_SEPARATOR = "/" ;这两个是文件分隔符
     然后给局部变量index赋值,通过查阅API:      
                    public int  indexOf(int ch)
               返回指定字符在此字符串中第一次出现处的索引。
     意思就是在path中查找"\\",例如我写文件的绝对路径是D:\\文件\\API\\JDK_API_1_6_zh_CN.CHM,我就需要循环的读取“\\”,接下来while循环中出现了substring方法,继续查阅API:
     
public  String  substring(int beginIndex)
返回一个新的字符串,它是此字符串的一个子字符串。该子字符串从指定索引处的字符开始,直到此字符串末尾。
public String substring(int beginIndex,
                        int endIndex)
返回一个新字符串,它是此字符串的一个子字符串。该子字符串从指定的 beginIndex 处开始,直到索引 endIndex - 1 处的字符。因此,该子字符串的长度为 endIndex-beginIndex
     故此 ,第一次循环会把path路径中从0索引开始,直到第一个"\\"之间的内容添加到StringBuffer中,然后再在StringBuffer中添加“/”,接下来pos和index都需要改变,要往后挪。因为循环需要往后走,我们要找到第二个“\\”,觉得这个有点算法的意思。返回sb.toString()。
总结一下replace方法,本意是根据传入的路径path,如果是D:\\文件\\API\\JDK_API_1_6_zh_CN.CHM这种格式的,给转换成D:/文件/API/JDK_API_1_6_zh_CN.CHM这种格式。
     8:StringUtils的cleanPath方法:
          通过replace的返回值,我们得到了可以用的路径pathToUse,然后我们要把这个路径下“:”给找出来,正如代码
          int prefixIndex = pathToUse .indexOf( ":" );
     那样,需要知道,indexOf方法只要没找到相应的字符,就会返回-1,所以在下面的判断中才会以perfixIndex是否为-1来进行判断。如果路            径中有“:”,接着以D:/文件/API/JDK_API_1_6_zh_CN.CHM举例,prefix="D:"  pathToUse="/文件/API/JDK_API_1_6_zh_CN.CHM ",这个很有意思,因为程序不知道我们输入的是绝对路径 带D:的这种 ,还是/开头的这种,或者说相对路径,程序直接全给你判断了。接下来会判断pathToUse是否以“/"开头,是的话prefix会加上“/”,现在的prefix有两种情况,可能是"D:/"这种,也可能是"/"这种,而pathToUse肯定是“文件/API/JDK_API_1_6_zh_CN.CHM ”这种了。
     String[] pathArray = delimitedListToStringArray( pathToUse , FOLDER_SEPARATOR );
           看到这句代码,我估计是把pathToUse给拆成字符串数组里,就像是这样,文件 API ***的这种。接下来看看具体的代码是不是这样:
  9:StringUtils的delimitedListToStringArray方法
 
     public static String[] delimitedListToStringArray(String str , String delimiter ) {
         return delimitedListToStringArray( str , delimiter , null );
    }
     
public static String[] delimitedListToStringArray(String str , String delimiter , String charsToDelete ) {
         if ( str == null ) {
             return new String[0];
        }
         if ( delimiter == null ) {
             return new String[] { str };
        }
        List<String> result = new ArrayList<String>();
         if ( "" .equals( delimiter )) {
             for ( int i = 0; i < str .length(); i ++) {
                 result .add(deleteAny( str .substring( i , i + 1), charsToDelete ));
            }
        }
         else {
             int pos = 0;
             int delPos ;
             while (( delPos = str .indexOf( delimiter , pos )) != -1) {
                 result .add(deleteAny( str .substring( pos , delPos ), charsToDelete ));
                 pos = delPos + delimiter .length();
            }
             if ( str .length() > 0 && pos <= str .length()) {
                 // Add rest of String, but not in case of empty input.
                 result .add(deleteAny( str .substring( pos ), charsToDelete ));
            }
        }
         return toStringArray( result );
    }
      先看看传入的参数:
          str:pathToUse,就是文件/API/JDK_API_1_6_zh_CN.CHM
                    delimiter:"/"
                    charsToDelete:null
               
     public static String deleteAny(String inString , String charsToDelete ) {
         if (!hasLength( inString ) || !hasLength( charsToDelete )) {
             return inString ;
        }
        StringBuilder sb = new StringBuilder();
         for ( int i = 0; i < inString .length(); i ++) {
             char c = inString .charAt( i );
             if ( charsToDelete .indexOf( c ) == -1) {
                 sb .append( c );
            }
        }
         return sb .toString();
    }
     如果说我们传入的"/"等于""的话,显然是不可能,我们所假如的话,会把pathTOUse倒着循环,每个字符都摘出来,然后当成字符串用,传入deleteAny中,然后又是循环,对每个字符而言,如果charsToDelete中没有这个字符,就在StringBuilder中添加这个字符。返回值是String。当然了,这个还没用到。我们用到的是那个很复杂的else
     我们遇到了一个循环,对pathToUse而言,从索引0开始,如果pathToUse中有"/",就像文件/API/JDK_API_1_6_zh_CN.CHM 我们会得到“文件,然后还会进入deleteAny这个方法,参数inString就是"文件",charsToDelete是null,突然发现charsToDelete的值为Null的话会直接返回InString,也就是“文件”。返回到 delimitedListToStringArray方法之后,接着往后循环,最终的结果就是实现了把pathToUse给切割成若干个String的形式。
     10:回到StringUtils的cleanPath方法
     我们遇到了一个倒着的循环,如果说我们这个是特别正常的路径,就相当于复制了,如果是以.或者..结尾的这些内容,我们就把它给忽略了。
     我得承认上面这些过程真的好复杂,其实就是做了一件事,对输入的路径进行了处理,只不过考虑的情况多了一点。所以我决定还是用debug来走一遍看看。
     这时我用的是绝对路径
   终于熬到了这个方法的结束。
  11:回到ClassPathResource的构造函数
     
this . classLoader = ( classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
         如果传入的classLoaser有值,就返回这个值,如果没有,就获取一个。
     
public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null ;
         try {
             cl = Thread.currentThread().getContextClassLoader();
        }
         catch (Throwable ex ) {
             // Cannot access thread context ClassLoader - falling back...
        }
         if ( cl == null ) {
             // No thread context class loader -> use class loader of this class.
             cl = ClassUtils. class .getClassLoader();
             if ( cl == null ) {
                 // getClassLoader() returning null indicates the bootstrap ClassLoader
                 try {
                     cl = ClassLoader.getSystemClassLoader();
                }
                 catch (Throwable ex ) {
                     // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
                }
            }
        }
         return cl ;
    }
     写到这,我们的ClassPathResouce resouce实例就有了path 和 classLoader这两个关键属性。
     如果我们想获取输入流
     @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 ;
    }
     会判断clazz 有没有值,classLoader有没有值,然后再获取输入流。前两天整理了java.lang.Class这个类的意思,现在就能用一点了,先看看定义的一些属性
     
     private final String path ;
 
     private ClassLoader classLoader ;
 
     private Class<?> clazz ;
     ClassPathResource有好几个构造函数,有的构造函数会传入classLoader,有的会传入clazz,这个clazz就是相应的类在JVM上的实例,显然上面的例子中并没有这个东西,而classLoader是有的,所以通过classLoader获取输入流。我觉得对ClassPathResource理解的更透彻了,虽然大部分时间都是在对path进行处理。近期还要看看ClassLoader,还不是很清楚它的工作机制。
  
 
 
     
      

转载于:https://www.cnblogs.com/zhao307/p/5413379.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ClassPathResource是一个用于从类路径中获取资源的类。它有多个构造函数,可以使用默认的类加载器或指定的类加载器来加载类路径下的资源。它可以表示存在于类路径中的文件系统或jar包中的资源。 示例代码中展示了如何使用ClassPathResource来读取类路径中的文件(例如1.txt)。在这个示例中,通过创建ClassPathResource对象并调用getFile()方法,可以判断资源是否存在。如果文件存在,返回true;否则返回false。 需要注意的是,ClassPathResource的加载是基于类路径的,因此要确保资源在类路径下才能正确加载。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spring ClassPathResource详解](https://blog.csdn.net/YangLiehui/article/details/98599253)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【Spring】Resource接口:ClassPathResource](https://blog.csdn.net/sayyy/article/details/120028877)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值