/* * JEF - Copyright 2009-2010 Jiyi (mr.jiyi@gmail.com) * * 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 jef.tools.reflect; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSource; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Collections; import java.util.List; import jef.common.log.LogUtil; import jef.tools.ArrayUtils; import jef.tools.Assert; import jef.tools.IOUtils; import jef.tools.JefConfiguration; import jef.tools.ResourceUtils; /** * 用于动态 编译、加载类的工具 * * ClassLoader: 记录了各个路径,并且集成加载类文件(.class),以及相关资源等行为特征的一个类,主要属性有 URL列表: * 这个列表记录了到什么地方去找文件,会按顺序遍历寻找。这个路径必须是目录或JAR文件,不能是其他的 parent: * 记录了上一层的ClassLoader,(JVM要求将所有的Classloader组织成一棵树),如果本处找不到类,会到上一Loader里去查找。 * * 基础常识: JVM启动后,默认有三个持久化的Classloader(以后简称系统Loader): <LI>BootstrapLoader * JVM核心,不允许访问。</LI> <LI>ExtClassloader:JVM下ext目录下的包和类。</LI> <LI> * SystemClassLoader: 又名AppClassLoader,记录当前应用程序的ClassPath</LI> 这三个的树型关系是 * BootstrapLoader 最顶层, ExtClassloader在中间, SystemClassLoader在最下这样的结构。 * 当我们使用Class.forName()时,就会到AppLoder去找class,实际效果是在这三个ClassLoader里面去寻找(当类载入前)。 * * 功能说明: 初始化后,即初始化了两个基本的ClassLoader,一个是系统ClassLoader,一个是扩展ClassCloader。 * 我们要载入一个动态类,有两种方法,一种是将该类所在路径加入到系统Loader当中去。 二是自己定义一个CLassLoader,用完后GC会回收这个资源。 * * @author Jiyi */ public class ClassLoaderUtil { /** * URLClassLoader本来是支持动态增加url(addUrl),并且可以访问其classes字段的,但是这两个都是受保护的( * protected), 因此初始化要先破除这两个东西的保护。 */ private static Field classes; private static Method addURL; static { try { addURL = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class }); addURL.setAccessible(true); } catch (Exception e) { LogUtil.show(e.getMessage()); } try { classes = ClassLoader.class.getDeclaredField("classes"); classes.setAccessible(true); } catch (Exception e) { LogUtil.show(e.getMessage()); } } /** * 得到当前线程的ContextClassLoader * * @return */ public ClassLoader getContextLoader() { return Thread.currentThread().getContextClassLoader(); } /** * 得到系统的ClassLoader * * @return */ public static URLClassLoader getAppClassLoader() { return (URLClassLoader) ClassLoader.getSystemClassLoader(); } /** * 得到虚拟机扩展的ClassLoader */ public static URLClassLoader getJvmExtClassLoader() { return (URLClassLoader) getAppClassLoader().getParent(); } /** * 得到由指定的ClassLoader所载入的所有类 * * @return */ @SuppressWarnings("unchecked") public static List<Class<?>> getClassesLoadedBy(ClassLoader cl) { if (classes == null) return Collections.EMPTY_LIST; try { List<Class<?>> list = (List<Class<?>>) classes.get(cl); return list; } catch (IllegalAccessException e) { throw new RuntimeException(e); } } /** * 构造一个URLClassLoader * * @param folder * @return */ public static URLClassLoader createURLClassLoader(File dirOrJar) { Assert.isTrue(dirOrJar.exists(), "the input file does not exist:" + dirOrJar.getPath()); if (dirOrJar.isFile()) { String ext = IOUtils.getExtName(dirOrJar.getName()); if (!ext.equals("jar") && !ext.equals("zip")) { dirOrJar = dirOrJar.getParentFile(); } } return createURLClassLoader(ResourceUtils.fileToURL(dirOrJar)); } /** * 构造一个URLClassLoader * * @param url * @return */ public static URLClassLoader createURLClassLoader(URL url) { URLClassLoader newloader = new URLClassLoader(new URL[] { url }, getAppClassLoader()); return newloader; } // /** // * 得到JRE基础的classpath (Bootstrap 的URL) // * // * @return // */ // @SuppressWarnings("restriction") // public static URL[] getBootstrapClassPath() { // return Launcher.getBootstrapClassPath().getURLs(); // } /** * 得到本工程的classpath * * @return */ public static URL[] getSystemClassPath() { return getAppClassLoader().getURLs(); } public static File[] getClasspath(ClassLoader cl) { String classLoaderName = cl.getClass().getSimpleName(); File[] fs = new File[0]; if (classLoaderName.equals("DefaultClassLoader")) {// RCP中的ClassLoader ClassLoader loader = JefConfiguration.class.getClassLoader(); fs = ClassLoaderUtil.getClasspathFromDefaultClassLoader(loader); } else if (cl instanceof URLClassLoader) { fs = ClassLoaderUtil.getClasspath((URLClassLoader) cl); } else { List<File> result = new ArrayList<File>(); try { for (URL u : ArrayUtils.toIterable(cl.getResources("."))) { result.add(IOUtils.urlToFile(u)); } } catch (IOException e) { LogUtil.exception(e); } fs = result.toArray(new File[result.size()]); } return fs; } /** * 将一个目录或JAR包添加到classpath * * @param path */ public static boolean addClassPath(String path) { return addClassPath(new File(path)); } /** * 将一个目录或JAR包添加到classpath * * @param dirOrJar */ public static boolean addClassPath(File dirOrJar) { return addClassPath(dirOrJar, getAppClassLoader()); } /** * 添加路径 * * @param dirOrJar * @param loader * @return */ public static void addUrl(URLClassLoader loader, URL... urls) { for (URL u : urls) { if (u != null) { try { addURL.invoke(loader, u); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e.getTargetException()); } } } } /** * 在URLClassLoader动态地增加URL * * @param dirOrJar * @param loader * @return */ public static boolean addClassPath(File dirOrJar, URLClassLoader loader) { try { URL url = ResourceUtils.fileToURL(dirOrJar); addURL.invoke(loader, new Object[] { url }); return true; } catch (Exception e) { LogUtil.exception(e); return false; } } /** * 从一个类得到这个类的class文件路径 * * @param c * @return */ public static String getClassFilePath(Class<?> c) { URL u = getCodeSource(c); if (u == null) return null; String path = u.getPath() + c.getName().replace('.', '/') + ".class"; return path; } /** * 得到一个类被加载时的路径 一个ClassLoader可以包含多个URL,加载时类可以从其中任意一个位置被读入 * * @param c * @return 可能返回null; */ public static URL getCodeSource(Class<?> c) { ProtectionDomain pd = c.getProtectionDomain(); CodeSource cs = pd.getCodeSource(); if (cs != null) return cs.getLocation(); if (!(c.getClassLoader() instanceof URLClassLoader)) return null; URLClassLoader ul = (URLClassLoader) c.getClassLoader(); for (URL url : ul.getURLs()) { File path = new File(url.getPath()); File classFile = new File(path, c.getName().replace('.', '/') + ".class"); if (classFile.exists()) { return url; } } return null; } public static void displayClassInfo(Class<?> clazz) { StringBuffer results = new StringBuffer(); displayClassInfo(clazz, results, true); System.out.println(results.toString()); } public static void displayClassInfo(Class<?> clazz, StringBuffer results, boolean showParentClassLoaders) { ClassLoader cl = clazz.getClassLoader(); results.append("\n" + clazz.getName() + "(" + Integer.toHexString(clazz.hashCode()) + ").ClassLoader=" + cl); ClassLoader parent = cl; while (parent != null) { results.append("\n.." + parent); if (parent instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) parent).getURLs(); int length = urls != null ? urls.length : 0; for (int u = 0; u < length; u++) { results.append("\n...." + urls[u]); } } if (showParentClassLoaders == false) break; if (parent != null) parent = parent.getParent(); } CodeSource clazzCS = clazz.getProtectionDomain().getCodeSource(); if (clazzCS != null) results.append("\n++++CodeSource: " + clazzCS); else results.append("\n++++Null CodeSource"); results.append("\nImplemented Interfaces:"); Class<?>[] ifaces = clazz.getInterfaces(); for (int i = 0; i < ifaces.length; i++) { Class<?> iface = ifaces[i]; results.append("\n++" + iface + "(" + Integer.toHexString(iface.hashCode()) + ")"); ClassLoader loader = ifaces[i].getClassLoader(); results.append("\n++++ClassLoader: " + loader); ProtectionDomain pd = ifaces[i].getProtectionDomain(); CodeSource cs = pd.getCodeSource(); if (cs != null) results.append("\n++++CodeSource: " + cs.getLocation()); else results.append("\n++++Null CodeSource"); } } /** * 对于RCP程序,其中ClassLoader是DefaultClassLoader,不是systemClassLoader * 此方法用于在RCP中获取路径 * * @param loader * @return */ public static File[] getClasspathFromDefaultClassLoader(ClassLoader loader) { Object manager = BeanUtils.getFieldValue(loader, "manager"); List<File> fs = new ArrayList<File>(); for (Object entry : (Object[]) BeanUtils.getFieldValue(manager, "entries")) { Object bf = BeanUtils.getFieldValue(entry, "bundlefile"); File f = (File) BeanUtils.getFieldValue(bf, "basefile"); fs.add(f); } return fs.toArray(new File[0]); } public static File[] getClasspathOfAppClassLoader() { return getClasspath(getAppClassLoader()); } /** * 得到指定的ClassLoader的路径 * * @param cl * @return */ public static File[] getClasspath(URLClassLoader cl) { List<File> fs = new ArrayList<File>(); for (URL url : cl.getURLs()) { File dirOrJar = IOUtils.urlToFile(url); fs.add(dirOrJar); } return fs.toArray(new File[0]); } }