/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * 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 com.alibaba.toolkit.util; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.URL; import java.util.Enumeration; import java.util.Set; import com.alibaba.toolkit.util.collection.ArrayHashSet; /** * <p> * 查找并装入类和资源的辅助类. * </p> * <p> * <code>ClassFinder</code>查找类和资源的效果, 相当于<code>ClassLoader.loadClass</code>方法和 * <code>ClassLoader.getResource</code>方法. 但<code>ClassFinder</code>总是首先尝试从 * <code>Thread.getContextClassLoader()</code>方法取得<code>ClassLoader</code> * 中并装入类和资源. 这种方法避免了在多级<code>ClassLoader</code>的情况下, 找不到类或资源的情况. * </p> * <p> * 假设有如下情况: * </p> * <ul> * <li>工具类<code>A</code>是从系统<code>ClassLoader</code>装入的(classpath)</li> * <li>类<code>B</code>是Web Application中的一个类, 是由servlet引擎的 * <code>ClassLoader</code>动态装入的</li> * <li>资源文件<code>C.properties</code>也在Web Application中, 只有servlet引擎的动态 * <code>ClassLoader</code>可以找到它</li> * <li>类<code>B</code>调用工具类<code>A</code>的方法, 希望通过类<code>A</code>取得资源文件 * <code>C.properties</code></li> * </ul> * <p> * 如果类<code>A</code>使用 * <code>getClass().getClassLoader().getResource("C.properties")</code> * , 就会失败, 因为系统<code>ClassLoader</code>不能找到此资源. * 但类A可以使用ClassFinder.getResource("C.properties"), 就可以找到这个资源, * 因为ClassFinder调用<code>Thread.currentThead().getContextClassLoader()</code> * 取得了servlet引擎的<code>ClassLoader</code>, 从而找到了这个资源文件. * </p> * <p> * 注意, <code>Thread.getContextClassLoader()</code>是在JDK1.2之后才有的, 对于低版本的JDK, * <code>ClassFinder</code>的效果和直接调用<code>ClassLoader</code>完全相同. * </p> * * @author Michael Zhou * @version $Id: ContextClassLoader.java,v 1.1 2003/07/03 07:26:15 baobao Exp $ */ public class ContextClassLoader { /** * JDK1.2以上, 这个变量保存了<code>Thread.getContextClassLoader()</code>方法. * 对于低版本的JDK, 此变量为<code>null</code>. */ private static Method GET_CONTEXT_CLASS_LOADER_METHOD = null; static { try { GET_CONTEXT_CLASS_LOADER_METHOD = Thread.class.getMethod("getContextClassLoader", (Class[]) null); } catch (NoSuchMethodException e) { // JDK 1.2以下. } } /** * <p> * 从<code>ClassLoader</code>取得所有resource URL. 按如下顺序查找: * </p> * <ol> * <li>在当前线程的<code>ClassLoader</code>中查找.</li> * <li>在装入自己的<code>ClassLoader</code>中查找.</li> * <li>通过<code>ClassLoader.getSystemResource</code>方法查找.</li> * </ol> * * @param resourceName 要查找的资源名, 就是以"/"分隔的标识符字符串 * @return resource的URL数组, 如果没找到, 则返回空数组. 数组中保证不包含重复的URL. */ public static URL[] getResources(String resourceName) { ClassLoader classLoader = null; Set urlSet = new ArrayHashSet(); boolean found = false; // 首先试着从当前线程的ClassLoader中查找. found = getResources(urlSet, resourceName, getClassLoader(), false); // 如果没找到, 试着从装入自己的ClassLoader中查找. if (!found) { getResources(urlSet, resourceName, ContextClassLoader.class.getClassLoader(), false); } // 最后的尝试: 在系统ClassLoader中查找(JDK1.2以上), // 或者在JDK的内部ClassLoader中查找(JDK1.2以下). if (!found) { getResources(urlSet, resourceName, null, true); } if (found) { return (URL[]) urlSet.toArray(new URL[urlSet.size()]); } return new URL[0]; } /** * 在指定class loader中查找指定名称的resource, 把所有找到的resource的URL放入指定的集合中. * * @param urlSet 存放resource URL的集合 * @param resourceName 资源名 * @param classLoader 类装入器 * @param sysClassLoader 是否用system class loader装载资源 * @return 如果找到, 则返回<code>true</code> */ private static boolean getResources(Set urlSet, String resourceName, ClassLoader classLoader, boolean sysClassLoader) { Enumeration i = null; try { if (classLoader != null) { i = classLoader.getResources(resourceName); } else if (sysClassLoader) { i = ClassLoader.getSystemResources(resourceName); } } catch (IOException e) { } if (i != null && i.hasMoreElements()) { while (i.hasMoreElements()) { urlSet.add(i.nextElement()); } return true; } return false; } /** * <p> * 从<code>ClassLoader</code>取得resource URL. 按如下顺序查找: * </p> * <ol> * <li>在当前线程的<code>ClassLoader</code>中查找.</li> * <li>在装入自己的<code>ClassLoader</code>中查找.</li> * <li>通过<code>ClassLoader.getSystemResource</code>方法查找.</li> * </ol> * * @param resourceName 要查找的资源名, 就是以"/"分隔的标识符字符串 * @return resource的URL */ public static URL getResource(String resourceName) { ClassLoader classLoader = null; URL url = null; // 首先试着从当前线程的ClassLoader中查找. classLoader = getClassLoader(); if (classLoader != null) { url = classLoader.getResource(resourceName); if (url != null) { return url; } } // 如果没找到, 试着从装入自己的ClassLoader中查找. classLoader = ContextClassLoader.class.getClassLoader(); if (classLoader != null) { url = classLoader.getResource(resourceName); if (url != null) { return url; } } // 最后的尝试: 在系统ClassLoader中查找(JDK1.2以上), // 或者在JDK的内部ClassLoader中查找(JDK1.2以下). return ClassLoader.getSystemResource(resourceName); } /** * 从<code>ClassLoader</code>取得resource的输入流. 相当于 * <code>getResource(resourceName).openStream()</code>. * * @param resourceName 要查找的资源名, 就是以"/"分隔的标识符字符串 * @return resource的输入流 */ public static InputStream getResourceAsStream(String resourceName) { URL url = getResource(resourceName); try { if (url != null) { return url.openStream(); } } catch (IOException e) { // 打开URL失败. } return null; } /** * 从当前线程的<code>ClassLoader</code>装入类. 对于JDK1.2以下, 则相当于 * <code>Class.forName</code>. * * @param className 要装入的类名 * @return 已装入的类 * @throws ClassNotFoundException 如果类没找到 */ public static Class loadClass(String className) throws ClassNotFoundException { return loadClass(className, true, null); } /** * 从指定的<code>ClassLoader</code>中装入类. 如果未指定<code>ClassLoader</code>, 则从当前线程的 * <code>ClassLoader</code>中装入. * * @param className 要装入的类名 * @param initialize 是否要初始化类 * @param classLoader 从指定的<code>ClassLoader</code>中装入类 * @return 已装入的类 * @throws ClassNotFoundException 如果类没找到 */ public static Class loadClass(String className, boolean initialize, ClassLoader classLoader) throws ClassNotFoundException { if (classLoader == null) { classLoader = getClassLoader(); } return Class.forName(className, initialize, classLoader); } /** * 取得当前线程的<code>ClassLoader</code>. 这个功能需要JDK1.2或更高版本的JDK的支持. * * @return 如果JDK是1.2以前版本, 返回null. 否则返回当前线程的<code>ClassLoader</code>. */ public static ClassLoader getClassLoader() { if (GET_CONTEXT_CLASS_LOADER_METHOD != null) { try { return (ClassLoader) GET_CONTEXT_CLASS_LOADER_METHOD.invoke(Thread.currentThread(), (Object[]) null); } catch (Throwable e) { return null; } } return null; } }