com.alibaba.toolkit.util.ContextClassLoader Maven / Gradle / Ivy
/*
* 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;
/**
*
* 查找并装入类和资源的辅助类.
*
*
* ClassFinder
查找类和资源的效果, 相当于ClassLoader.loadClass
方法和
* ClassLoader.getResource
方法. 但ClassFinder
总是首先尝试从
* Thread.getContextClassLoader()
方法取得ClassLoader
* 中并装入类和资源. 这种方法避免了在多级ClassLoader
的情况下, 找不到类或资源的情况.
*
*
* 假设有如下情况:
*
*
* - 工具类
A
是从系统ClassLoader
装入的(classpath)
* - 类
B
是Web Application中的一个类, 是由servlet引擎的
* ClassLoader
动态装入的
* - 资源文件
C.properties
也在Web Application中, 只有servlet引擎的动态
* ClassLoader
可以找到它
* - 类
B
调用工具类A
的方法, 希望通过类A
取得资源文件
* C.properties
*
*
* 如果类A
使用
* getClass().getClassLoader().getResource("C.properties")
* , 就会失败, 因为系统ClassLoader
不能找到此资源.
* 但类A可以使用ClassFinder.getResource("C.properties"), 就可以找到这个资源,
* 因为ClassFinder调用Thread.currentThead().getContextClassLoader()
* 取得了servlet引擎的ClassLoader
, 从而找到了这个资源文件.
*
*
* 注意, Thread.getContextClassLoader()
是在JDK1.2之后才有的, 对于低版本的JDK,
* ClassFinder
的效果和直接调用ClassLoader
完全相同.
*
*
* @author Michael Zhou
* @version $Id: ContextClassLoader.java,v 1.1 2003/07/03 07:26:15 baobao Exp $
*/
public class ContextClassLoader {
/**
* JDK1.2以上, 这个变量保存了Thread.getContextClassLoader()
方法.
* 对于低版本的JDK, 此变量为null
.
*/
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以下.
}
}
/**
*
* 从ClassLoader
取得所有resource URL. 按如下顺序查找:
*
*
* - 在当前线程的
ClassLoader
中查找.
* - 在装入自己的
ClassLoader
中查找.
* - 通过
ClassLoader.getSystemResource
方法查找.
*
*
* @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 如果找到, 则返回true
*/
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;
}
/**
*
* 从ClassLoader
取得resource URL. 按如下顺序查找:
*
*
* - 在当前线程的
ClassLoader
中查找.
* - 在装入自己的
ClassLoader
中查找.
* - 通过
ClassLoader.getSystemResource
方法查找.
*
*
* @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);
}
/**
* 从ClassLoader
取得resource的输入流. 相当于
* getResource(resourceName).openStream()
.
*
* @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;
}
/**
* 从当前线程的ClassLoader
装入类. 对于JDK1.2以下, 则相当于
* Class.forName
.
*
* @param className 要装入的类名
* @return 已装入的类
* @throws ClassNotFoundException 如果类没找到
*/
public static Class loadClass(String className) throws ClassNotFoundException {
return loadClass(className, true, null);
}
/**
* 从指定的ClassLoader
中装入类. 如果未指定ClassLoader
, 则从当前线程的
* ClassLoader
中装入.
*
* @param className 要装入的类名
* @param initialize 是否要初始化类
* @param classLoader 从指定的ClassLoader
中装入类
* @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);
}
/**
* 取得当前线程的ClassLoader
. 这个功能需要JDK1.2或更高版本的JDK的支持.
*
* @return 如果JDK是1.2以前版本, 返回null. 否则返回当前线程的ClassLoader
.
*/
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;
}
}