Java项目查找非限定字符串引用的类--请求评论

标签 java class reflection project

我正在开展一个个人项目,希望该项目会发展成为一个巨大而可怕的项目......

我想派生字符串引用的类,即使它不是完全限定的。我想尽可能地重现 JVM 的行为。例如,在代码中我可以引用已导入或位于同一包中的不合格的东西。我知道这可能不完全可能,但我想尝试一下。我当前的代码存在以下问题:

  • 速度很慢。它生成所有类的列表,并在其中查找匹配项。 在我的机器上大约需要 6 秒。 更新:我更改了 Reflection 代码以获取一组类名而不是类。现在速度大约是原来的两倍。
  • 显然这也意味着我可能会遇到重复的麻烦。有没有一种方法可以像 Java 用来消除歧义一样对列表进行排序? 更新这是唯一剩下的严重问题。至少如果发现有歧义,我现在会抛出异常。
  • 我使用 Google Reflection 库来派生类列表。我使用 ClasspathHelper.forClassLoader()ClasspathHelper.forJavaClassPath(),但我仍然可能缺少一些。 更新我正在联合四个不同的类集,它们似乎提供了良好的覆盖范围。
  • 我确实缺少 java.*。这是因为这些类是由原始类加载器加载的,并且可以进行硬编码。因此它们不可用?作为最后的手段,java.* 中是否有机器可读形式的类列表,我可以用它来查找它们? 更新 http://docs.oracle.com/javase/7/docs/api/overview-summary.html 似乎是迄今为止最好的选择。显然,我还可以编写一个 doclet,它将以机器可读的形式输出 javadoc。已经有了吗?

还有其他问题吗?这是当前代码:

/**
 * Attempts to return the class specified by a string even if the it is not
 * fully qualified.  It does this by going through all the classes there are.
 * Note: You may specify arrays in normal declaration form, e.g. myArray[][].
 * 
 * @param classString       The string rep of the class/type
 * @return                  The class I think it refers to
 */
private static Class<?> getClassOfString(String classString) {
    classString = convertTypeToCanonicalForm(classString);
    Matcher matcher = Pattern.compile("(\\[+(\\w))?((\\w+(\\.\\w+)*);?)?").matcher(classString);
    matcher.find();
    String arrayPrefix = matcher.group(2);
    String className = matcher.group(4);
    try {
        if (arrayPrefix == null || arrayPrefix.equals("L")) {
            String classFound = null;
            for (String clazz : getSetOfAllClasses()) {
                if (clazz != null) {
                    if (clazz.matches("(.*\\.)?"+Pattern.quote(className)))
                        if (classFound == null)
                            classFound = clazz;
                        else
                            throw new RuntimeException("Class name '" + className +
                                    "' is ambiguous: " + classFound + " vs " + clazz);
                }
            }
            if (classFound != null) {
                classString = classString.replaceAll(Pattern.quote(className), classFound);
                return Class.forName(classString);
            }
        }
    } catch (ClassNotFoundException e) {
        throw new RuntimeException();
    }

        if (arrayPrefix == null)
            if (className == "boolean") return(boolean.class);          // primitive types
            else if (className == "byte") return(byte.class);
            else if (className == "char") return(char.class);
            else if (className == "double") return(double.class);
            else if (className == "float") return(float.class);
            else if (className == "int") return(int.class);
            else if (className == "long") return(long.class);
            else if (className == "short") return(short.class);
            else if (className == "void") return(void.class);

        // hack for java.* types-- look 'em up
        if (className != null) {
            String prefixFound = null;
            for (String prefix : javaTypes.keySet())
                for (String type : javaTypes.get(prefix))
                    if ((prefix+"."+type).matches(".*" + className))
                        if (prefixFound == null)
                            prefixFound = prefix;
                        else
                            throw new RuntimeException("Class name '" + className +
                                "' is ambiguous: java." + prefixFound + "." + className +
                                " vs java." + prefix + "." + className);
            if (prefixFound == null) prefixFound = "util";      // temp hack
            classString = classString.replaceAll(Pattern.quote(className), 
                    "java." + prefixFound + "." + className);
        }

        try {
            return Class.forName(classString);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Class '" + className + 
                    "' is unknown or somewhere in java.* that I don't know about");
        }
}

/**
 * Converts the type from standard declaration form to canonical internal form
 * if necessary.  Practically this just means doing array translation.
 * 
 * @param type
 * @return
 */
private static String convertTypeToCanonicalForm(String type) {
    Matcher matcher = Pattern.compile("^(\\w+(\\.\\w+)*)((\\[\\])+)$").matcher(type);
    if (matcher.find()) {
        String typeTemp = matcher.group(1);
        if (typeTemp.equals("boolean")) typeTemp = "Z";         // primitive typeTemps
        else if (typeTemp.equals("byte")) typeTemp = "B";
        else if (typeTemp.equals("char")) typeTemp = "C";
        else if (typeTemp.equals("double")) typeTemp = "D";
        else if (typeTemp.equals("float")) typeTemp = "F";
        else if (typeTemp.equals("int")) typeTemp = "I";
        else if (typeTemp.equals("long")) typeTemp = "J";
        else if (typeTemp.equals("short")) typeTemp = "S";
        else typeTemp = "L" + typeTemp + ";";

        matcher = Pattern.compile("\\[\\]").matcher(matcher.group(3));
        while (matcher.find())
            typeTemp = "[" + typeTemp;
        type = typeTemp;
    }
    return type;
}

/**
 * List of package classes for each prefix in the java.* domain
 */
@SuppressWarnings("serial") 
static final Map<String, List<String>> javaTypes = new HashMap<String , List<String>>() {{
    put("lang",         Arrays.asList(new String[]{"Boolean","Byte","Character","Class","Double",
            "Float","Integer","Long","Short","String","Void"}));
    // rest of java.* goes here
}};

/**
 * Gets and stores a set of all the classes we can find.  Missing the java.* domain.
 * Uses the Google Reflection library.
 * 
 * @return          The class set
 */
static Set<String> classStringSet = null;
private static Set<String> getSetOfAllClasses() {

    if (classStringSet == null) {
        List<ClassLoader> classLoadersList = new LinkedList<ClassLoader>();
        classLoadersList.add(ClasspathHelper.contextClassLoader());
        classLoadersList.add(ClasspathHelper.staticClassLoader());                      
        classLoadersList.add(ClassLoader.getSystemClassLoader());                      
        Reflections reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(new SubTypesScanner(false), new ResourcesScanner())
            .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0]))));
        classStringSet = reflections.getStore().getSubTypesOf(Object.class.getName());

        reflections = new Reflections(new ConfigurationBuilder()
            .setScanners(new SubTypesScanner(false), new ResourcesScanner())
            .setUrls(ClasspathHelper.forJavaClassPath()));
        classStringSet.addAll(reflections.getStore().getSubTypesOf(Object.class.getName()));

    }

return classStringSet;
}

我为什么要这么做?那一定会很好玩!你会看到的。

最佳答案

It is slow. It generates a list of all the classes there are and looks for matches in it. Takes about 6 seconds on my machine.

该实现可能会遍历类路径/bootclasspath 上的所有目录以及所有文件的索引。 (虽然 6 秒看起来确实有点太多了……除非你的类路径上有大量的类。)

如果每次需要 6 秒,则应考虑将类名集缓存在合适的数据结构中。

Obviously this also means that I can have duplicate troubles. Is there a way to order the list the same way Java uses to disambiguate things?

Java 不会消除名称的歧义。如果发生冲突,则为编译错误。

(实际上,我过于简单化了。该语言具有管理名称处理的规则,但这些规则很大程度上取决于源代码中的 import 语句。您是否有类似的东西在您的用例中导入语句?)

I am certainly missing java.*. This is because those classes are loaded by the primordial classloader and can be hard coded. They are therefore not available?

您应该能够使用 getSystemClassLoader() 来获取系统类加载器

关于Java项目查找非限定字符串引用的类--请求评论,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14330747/

相关文章:

Java Spring,如何检索记录并使用新的唯一键发布重复项?

c++ - 类实例的内存分配——应该使用继承来减少内存消耗吗?

c++ - 调用使用字符数组的类函数 (C++)

java - java中数组的反射

c# - 通过反射访问 TextSegment 的值

Java未捕获异常

java - com.fasterxml.jackson.core.JsonGenerationException : Can not write a field name, 期待一个值

java - 如何在 springboot 2.x 中处理 @pathvariable 中的编码 url(包含特殊字符,如 %2F)?

java - 如何通过套接字连接发送实例?

powershell - Powershell/.net反射-GetMethod()查找常规方法,但找不到通用方法-为什么?