001package net.gdface.utils; 002 003import java.io.File; 004import java.io.FileFilter; 005import java.net.MalformedURLException; 006import java.net.URL; 007import java.net.URLClassLoader; 008import java.util.Collection; 009import java.util.HashSet; 010import java.util.Set; 011 012public class ClassLoaderUtils { 013 /** 014 * {@code parent}为{@code null}æ—¶ç–ç•¥<br> 015 * {@code defaultParentLoader} 使用default parent class loader,å‚è§{@link URLClassLoader#newInstance(URL[])}<br> 016 * {@code threadContextLoader} 使用当å‰çº¿ç¨‹çš„Thread Context ClassLoader作为parent,å‚è§{@link Thread#getContextClassLoader()} <br> 017 * {@code currentClassLoader} 使用当å‰ç±»({@link ClassLoaderUtils})çš„class loader<br> 018 * @author guyadong 019 * 020 */ 021 public static enum ParentStrategy{ 022 defaultParentLoader, threadContextLoader,currentClassLoader 023 } 024 /** 025 * 默认值 {@link ParentStrategy#currentClassLoader } 026 * @see {@link ParentStrategy} 027 */ 028 private static final ThreadLocal<ParentStrategy> parentLoaderStrategy = new ThreadLocal<ParentStrategy>(){ 029 @Override 030 protected ParentStrategy initialValue() { 031 return ParentStrategy.currentClassLoader; 032 }}; 033 034 private static final boolean addIfJar(Collection<URL> out, File file) { 035 if (file.isFile() && file.getName().endsWith(".jar")) { 036 out.add(toURL(file)); 037 return true; 038 } 039 return false; 040 } 041 042 private static final URL toURL(File file) { 043 try { 044 return file.toURI().toURL(); 045 } catch (MalformedURLException e) { 046 throw new RuntimeException(e); 047 } 048 } 049 050 /** 051 * 返回文件{@link URL}集åˆ<br> 052 * 如果 {@code file} 是文件夹,则文件夹所有jar包文件生æˆ{@link URL} 集åˆ<br> 053 * 如果{@code file}是jar,则将返回包å«è¯¥jarçš„{@link URL} 集åˆ<br> 054 * å¦‚æžœä¸æ˜¯jar抛出异常 055 * 056 * @param file 057 * ä¸å˜åœ¨æ—¶æŠ›å‡ºå¼‚常 058 * @param recursive 059 * 为 {@code true}时递归æœç´¢å目录,{@code file}为文件夹时有效 060 * @return 061 */ 062 private static final Set<URL> toJarURLs(File file, final boolean recursive) { 063 final HashSet<URL> out = new HashSet<URL>(); 064 if (!file.exists()) 065 throw new IllegalArgumentException(" not exists:" + file.toString()); 066 if (file.isFile()) { 067 if (!addIfJar(out, file)) 068 throw new IllegalArgumentException(" not jar:" + file.toString()); 069 } 070 if (file.isDirectory()) { 071 file.listFiles(new FileFilter() { 072 @Override 073 public boolean accept(File pathname) { 074 if (!addIfJar(out, pathname) && pathname.isDirectory() && recursive) { 075 out.addAll(toJarURLs(pathname, recursive)); 076 } 077 return false; 078 } 079 }); 080 return out; 081 } 082 throw new IllegalArgumentException(" invalid file:" + file.toString()); 083 } 084 085 private final static Set<URL> toJarURLs(String path, final boolean recursive) { 086 if (null == path || 0 == path.length()) 087 throw new IllegalArgumentException("path must not be null or empty "); 088 return toJarURLs(new File(path), recursive); 089 } 090 091 private final static Set<URL> toJarURLs(final boolean recursive, String... paths) { 092 if (null == paths) 093 throw new NullPointerException("paths is null"); 094 HashSet<URL> out = new HashSet<URL>(); 095 for (String path : paths) { 096 out.addAll(toJarURLs(path, recursive)); 097 } 098 return out; 099 } 100 101 private final static Set<URL> toJarURLs(String... classpath) { 102 if (null == classpath) 103 throw new NullPointerException("classpaths is null"); 104 final HashSet<URL> out = new HashSet<URL>(); 105 for (String path : classpath) { 106 if (null == path || 0 == path.length()) 107 throw new IllegalArgumentException("classPaths have null or empty element"); 108 File file = new File(path); 109 if (!file.exists()) 110 throw new IllegalArgumentException("no exists : " + file); 111 if (file.isFile()) { 112 if (!addIfJar(out, file)) 113 throw new IllegalArgumentException("not jar:" + file); 114 } else if (file.isDirectory()) 115 out.add(toURL(file)); 116 } 117 return out; 118 } 119 120 private final static Set<URL> toJarURLs(boolean recursive, String[] libdirs, String[] classpath) { 121 HashSet<URL> out = new HashSet<URL>(); 122 if (null != libdirs) { 123 out.addAll(toJarURLs(recursive, libdirs)); 124 } 125 if (null != classpath) 126 out.addAll(toJarURLs(classpath)); 127 return out; 128 } 129 130 /** @see #makeURLClassLoader(ClassLoader, boolean, String[], String[]) */ 131 public final static URLClassLoader makeURLClassLoader(ClassLoader parent, String... classpath) { 132 return makeURLClassLoader(parent, false, null, classpath); 133 } 134 135 /** @see #makeURLClassLoader(ClassLoader, boolean, String[], String[]) */ 136 public final static URLClassLoader makeURLClassLoader(ClassLoader parent, boolean recursive, String... libdirs) { 137 return makeURLClassLoader(parent, recursive, libdirs, null); 138 } 139 140 /** 141 * æ ¹æ®{@code libdirs}æä¾›çš„lib路径和{@code classpath}创建{@link URLClassLoader}实例<br> 142 * å¦‚æžœæ‰€æœ‰çš„å‚æ•°ä¸éƒ½æ²¡æœ‰æ‰¾URL(jar或class 文件夹),则抛出异常 143 * @param parent æŒ‡å®šçˆ¶ç±»åŠ è½½å™¨,为nullæ—¶æ ¹æ®{@link #parentLoaderStrategy}决定parent 144 * @param recursive 145 * 指示是å¦é€’å½’æœç´¢æ–‡ä»¶å¤¹,对 {@code libdirs}有效,see also 146 * {@link #toJarURLs(File, boolean)} 147 * @param libdirs 148 * path列表,path为jar包或jar所在文件夹(such as 'lib') 149 * @param classpath 150 * jar包或class文件夹路径 151 * 152 * @return 153 * @see {@link URLClassLoader#newInstance(URL[],ClassLoader)} 154 * @see {@linkplain Thread#getContextClassLoader()} 155 */ 156 public final static URLClassLoader makeURLClassLoader(ClassLoader parent, boolean recursive, String[] libdirs, String[] classpath) { 157 Set<URL> out = toJarURLs(recursive, libdirs, classpath); 158 if (out.isEmpty()) 159 throw new IllegalArgumentException("empty libdirs and classpath"); 160 if(null == parent){ 161 switch(parentLoaderStrategy.get()){ 162 case defaultParentLoader: 163 return URLClassLoader.newInstance(out.toArray(new URL[out.size()])); 164 case threadContextLoader: 165 parent = Thread.currentThread().getContextClassLoader(); 166 break; 167 case currentClassLoader: 168 parent = ClassLoaderUtils.class.getClassLoader(); 169 break; 170 } 171 } 172 return URLClassLoader.newInstance(out.toArray(new URL[out.size()]),parent); 173 } 174 /** @see #makeURLClassLoader(ClassLoader, boolean, String[], String[]) */ 175 public final static URLClassLoader makeURLClassLoader(ClassLoader parent, boolean recursive, 176 Collection<String> libdirs, Collection<String> classpath) { 177 return makeURLClassLoader(parent, recursive, 178 null == libdirs ? null : libdirs.toArray(new String[0]), null == classpath ? null : classpath.toArray(new String[0])); 179 } 180 /** 181 * @param parentLoaderStrategy if null,{@link ParentStrategy#currentClassLoader} instead 182 * @see #makeURLClassLoader(ClassLoader, boolean, String[], String[]) 183 * @see #parentLoaderStrategy 184 */ 185 public final static void setParentLoaderStrategy(ParentStrategy parentLoaderStrategy){ 186 ClassLoaderUtils.parentLoaderStrategy.set(null == parentLoaderStrategy 187 ?ParentStrategy.currentClassLoader 188 :parentLoaderStrategy); 189 } 190 /** @see #setParentLoaderStrategy(ParentStrategy) */ 191 public final static void setParentLoaderStrategy(String parentLoaderStrategy){ 192 setParentLoaderStrategy(ParentStrategy.valueOf(parentLoaderStrategy)); 193 } 194}