运行时(at runtime)找到Class对象的classfile location

以下代码参考
http://www.javaworld.com/javaworld/javaqa/2003-07/01-qa-0711-classsrc.html

在项目中经常会碰到jar包冲突的情况,有时候想知道jvm到底是加载了哪个jar里面的对应类,下面这段代码就要派上用场鸟,呼呼
代码清晰易懂,我就不多做解释了,记录下来留着自己以后用
    /**
     * Given a Class<?> object, attempts to find its .class location [returns
     * null if no such definiton could be found].
     * 
     * @return URL that points to the class definition [null if not found]
     */
    public static URL getClassLocation(final Class<?> cls) {
        if (cls == null)
            throw new IllegalArgumentException("null input: cls");
        URL result = null;
        final String clsAsResource = cls.getName().replace('.', '/').concat(".class");
        final ProtectionDomain pd = cls.getProtectionDomain();
        // java.lang.Class<?> contract does not specify if 'pd' can ever be
        // null;
        // it is not the case for Sun's implementations, but guard against null
        // just in case:
        if (pd != null) {
            final CodeSource cs = pd.getCodeSource();
            // 'cs' can be null depending on the classloader behavior:
            if (cs != null)
                result = cs.getLocation();
            if (result != null) {
                // convert a code source location into a full class file
                // location
                // for some common cases:
                if ("file".equals(result.getProtocol())) {
                    try {
                        if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip"))
                            result = new URL("jar:".concat(result.toExternalForm()).concat("!/").concat(clsAsResource));
                        else if (new File(result.getFile()).isDirectory())
                            result = new URL(result, clsAsResource);
                    } catch (MalformedURLException ignore) { }
                }
            }
        }
        if (result == null) {
            // try to find 'cls' definition as a resource; this is not
            // documented to be legal but Sun's implementations seem to allow
            // this:
            final ClassLoader clsLoader = cls.getClassLoader();
            result = clsLoader != null ? clsLoader.getResource(clsAsResource) : ClassLoader.getSystemResource(clsAsResource);
        }
        return result;
    }


http://www.nakov.com/wp-content/uploads/2008/08/classscopejava.html
这个ClassScope类提供了除getClassLocation方法外其他的一些常用方法
import java.io.File;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;

/**
 * A simple static API for listing classes loaded in a JVM. Based on
 * {@link #getClassLocation(Class)} from
 * http://www.javaworld.com/javaworld/javaqa/2003-07/01-qa-0711-classsrc.html
 * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003
 */
public class ClassScope {
    
    private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
    private static final Throwable CVF_FAILURE, CR_FAILURE; // set in <clinit>
            
    static {
        Throwable failure = null;
        Field tempf = null;
        try {
            // this can fail if this is not a Sun-compatible JVM
            // or if the security is too tight:
            tempf = ClassLoader.class.getDeclaredField("classes");
            if (tempf.getType() != Vector.class) {
                throw new RuntimeException("not of type java.util.Vector: " + tempf.getType().getName());
            }
            tempf.setAccessible(true);
        } catch (Throwable t) {
            failure = t;
        }
        
        CLASSES_VECTOR_FIELD = tempf;
        CVF_FAILURE = failure;
        failure = null;
        CallerResolver tempcr = null;
        
        try {
            // this can fail if the current SecurityManager does not allow
            // RuntimePermission ("createSecurityManager"):
            tempcr = new CallerResolver();
        } catch (Throwable t) {
            failure = t;
        }
        CALLER_RESOLVER = tempcr;
        CR_FAILURE = failure;
    }
    
    /**
     * Given a class loader instance, returns all classes currently loaded by
     * that class loader.
     * 
     * @param defining class loader to inspect [may not be null]
     * @return Class array such that every Class has 'loader' as its defining
     *         class loader [never null, may be empty]
     * @throws RuntimeException
     *             if the "classes" field hack is not possible in this JRE
     */
    @SuppressWarnings("unchecked")
    public static Class<?>[] getLoadedClasses(final ClassLoader loader) {
        if (loader == null) {
            throw new IllegalArgumentException("null input: loader");
        }
        if (CLASSES_VECTOR_FIELD == null) {
            throw new RuntimeException("ClassScope::getLoadedClasses() cannot be used in this JRE", CVF_FAILURE);
        }
        
        try {
            final Vector<Class<?>> classes = (Vector<Class<?>>) CLASSES_VECTOR_FIELD.get(loader);
            if (classes == null)
                return EMPTY_CLASS_ARRAY;
            final Class<?>[] result;
            // note: Vector is synchronized in Java 2, which helps us make
            // the following into a safe critical section:
            synchronized (classes) {
                result = new Class<?>[classes.size()];
                classes.toArray(result);
            }
            return result;
        }
        // this should not happen if <clinit> was successful:
        catch (IllegalAccessException e) {
            e.printStackTrace(System.out);
            return EMPTY_CLASS_ARRAY;
        }
        
    }
    
    /**
     * A convenience multi-loader version of
     * {@link #getLoadedClasses(ClassLoader)}.
     * 
     * @param an array of defining class loaders to inspect [may not be null]
     * @return Class<?> array [never null, may be empty]
     * @throws RuntimeException
     *             if the "classes" field hack is not possible in this JRE
     */
    public static Class<?>[] getLoadedClasses(final ClassLoader[] loaders) {
        if (loaders == null)
            throw new IllegalArgumentException("null input: loaders");
        final List<Class<?>> resultList = new LinkedList<Class<?>>();
        for (int l = 0; l < loaders.length; ++l) {
            final ClassLoader loader = loaders[l];
            if (loader != null) {
                final Class<?>[] classes = getLoadedClasses(loaders[l]);
                resultList.addAll(Arrays.asList(classes));
            }
        }
        final Class<?>[] result = new Class<?>[resultList.size()];
        resultList.toArray(result);
        return result;
    }
    
    /**
     * Returns the class loader set "relevant" to the calling class, as
     * described in the article. Starting with the class that is the caller of
     * this method, it collects all class loaders that are loaders for all
     * classes on the call stack and their respective parent loaders.
     * 
     * @return ClassLoader array [never null]
     * @throws RuntimeException
     *             if the caller context resolver could not be instantiated
     */
    public static ClassLoader[] getCallerClassLoaderTree() {
        if (CALLER_RESOLVER == null)
            throw new RuntimeException("Class<?>Scope::getCallerClassLoaderTree() cannot be used in this JRE", CR_FAILURE);
        final Class<?>[] callContext = CALLER_RESOLVER.getClassContext();
        final Set<ClassLoader> resultSet = new HashSet<ClassLoader>();
        for (int c = 2; c < callContext.length; ++c) {
            getClassLoaderTree(callContext[c], resultSet);
        }
        final ClassLoader[] result = new ClassLoader[resultSet.size()];
        resultSet.toArray(result);
        return result;
    }
    
    /**
     * Given a Class<?> object, attempts to find its .class location [returns
     * null if no such definiton could be found].
     * 
     * @return URL that points to the class definition [null if not found]
     */
    public static URL getClassLocation(final Class<?> cls) {
        if (cls == null)
            throw new IllegalArgumentException("null input: cls");
        URL result = null;
        final String clsAsResource = cls.getName().replace('.', '/').concat(".class");
        final ProtectionDomain pd = cls.getProtectionDomain();
        // java.lang.Class<?> contract does not specify if 'pd' can ever be
        // null;
        // it is not the case for Sun's implementations, but guard against null
        // just in case:
        if (pd != null) {
            final CodeSource cs = pd.getCodeSource();
            // 'cs' can be null depending on the classloader behavior:
            if (cs != null)
                result = cs.getLocation();
            if (result != null) {
                // convert a code source location into a full class file
                // location
                // for some common cases:
                if ("file".equals(result.getProtocol())) {
                    try {
                        if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip"))
                            result = new URL("jar:".concat(result.toExternalForm()).concat("!/").concat(clsAsResource));
                        else if (new File(result.getFile()).isDirectory())
                            result = new URL(result, clsAsResource);
                    } catch (MalformedURLException ignore) { }
                }
            }
        }
        if (result == null) {
            // try to find 'cls' definition as a resource; this is not
            // documented to be legal but Sun's implementations seem to allow
            // this:
            final ClassLoader clsLoader = cls.getClassLoader();
            result = clsLoader != null ? clsLoader.getResource(clsAsResource) : ClassLoader.getSystemResource(clsAsResource);
        }
        return result;
    }
    
    /**
     * A helper class to get the call context. It subclasses SecurityManager to
     * make getClassContext() accessible. An instance of CallerResolver only
     * needs to be created, not installed as an actual security manager.
     */
    private static final class CallerResolver extends SecurityManager {
        protected Class<?>[] getClassContext() {
            return super.getClassContext();
        }
    } // end of nested class
    
    private ClassScope() { } // this class is not extendible
    
    private static void getClassLoaderTree(final Class<?> cls, final Set<ClassLoader> resultSet) {
        if ((cls != null) && (resultSet != null)) {
            for (ClassLoader loader = cls.getClassLoader(); loader != null; loader = loader.getParent()) {
                resultSet.add(loader);
            }
        }
    }
    
    private static final Field CLASSES_VECTOR_FIELD;        // set in <clinit> [can be null]
    private static final CallerResolver CALLER_RESOLVER;    // set in <clinit> [can be null]
}

猜你喜欢

转载自budairenqin.iteye.com/blog/1667278