java - Field#getGenericType() 抛出 java.lang.TypeNotPresentException

标签 java maven reflection annotations

我正在编写一个 Maven 插件,我需要知道 List<Person> 的类型哪里Person是在依赖项中定义的对象。该插件在process-classes期间运行阶段根据在宿主项目中带注释的类上找到的内容生成一些文件。在这种情况下,宿主项目包含对其他项目中的库的引用,这些库作为 Maven 依赖项包含在内。

Person类:

package com.dependency.models;    

public class Person {
    // Irrelevent
}

我正在执行查询的类中的使用:

package com.project.wrappers;

import com.dependency.Person;    

@MyAnnotation
public class Wrapper {
     List<Person> people;
     // Other irrelevant stuff
}

注释:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE})
public @interface MyAnnotation {}

从 Maven 插件使用上下文中,我加载所有 Wrapper通过注释的对象然后进行处理。为此,我需要通过 Reflections 使用自定义类加载器图书馆:

public static Set<Class<?>> findAnnotatedClasses(MavenProject mavenProject, Class<? extends Annotation> input) throws MojoExecutionException {
    List<String> classpathElements = null;

    try {
        classpathElements = mavenProject.getCompileClasspathElements();
        List<URL> projectClasspathList = new ArrayList<URL>();
        for (String element : classpathElements) {
            Application.getLogger().debug("Considering compile classpath element (via MavenProject): " + element);
            try {
                projectClasspathList.add(new File(element).toURI().toURL());
            } catch (MalformedURLException e) {
                throw new MojoExecutionException(element + " is an invalid classpath element", e);
            }
        }

        // Retain annotations
        JavassistAdapter javassistAdapter = new JavassistAdapter();
        javassistAdapter.includeInvisibleTag = false;

        URLClassLoader urlClassLoader = new URLClassLoader(projectClasspathList.toArray(new URL[]{}),
                Thread.currentThread().getContextClassLoader());

        Reflections reflections = new Reflections(
                new ConfigurationBuilder().setUrls(
                        ClasspathHelper.forClassLoader(urlClassLoader)
                ).addClassLoader(urlClassLoader).setScanners(new TypeAnnotationsScanner(), new TypeElementsScanner(),
                        new FieldAnnotationsScanner(), new TypeAnnotationsScanner(), new SubTypesScanner(false)
                ).setMetadataAdapter(javassistAdapter)
        );

        return findAnnotatedClasses(reflections, input);

    } catch (DependencyResolutionRequiredException e) {
        throw new MojoExecutionException("Dependency resolution failed", e);
    }
}

到目前为止一切都很好。但是如果我在解析器中尝试以下操作,它就会失败:

Field field = Wrapper.class.getDeclaredField("people");
ParameterizedType listType = (ParameterizedType) field.getGenericType();
Class<?> listTypeClass = (Class<?>) listType.getActualTypeArguments()[0];

抛出以下异常:

Caused by: java.lang.TypeNotPresentException: Type com.dependency.models.Person not present
at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at sun.reflect.generics.repository.FieldRepository.getGenericType(FieldRepository.java:85)
at java.lang.reflect.Field.getGenericType(Field.java:247)

如果进一步如下:

Caused by: java.lang.ClassNotFoundException: com.dependency.models.Person
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:114)

这意味着我的类加载适用于最后一步之前的所有内容。我想我需要一种方法来覆盖 sun.reflect 中使用的类加载器图书馆,但这可能是不可能的。关于更好的方法有什么建议吗?

更新

我已将依赖项 jar 添加到 URLClassLoader并试图覆盖上面CoreReflectionFactory正在使用的当前类加载器无济于事。

最佳答案

解决方案不仅仅是将依赖项添加到类加载器,而是实际解析 jar 路径,然后将这些 jar 的路径添加到反射库的类路径中,以将其包含在扫描中。

因此,从 Maven 获取所有 Artifact ,然后在构建该 Artifact 时解析并将每个路径添加到 Reflections。像这样的事情:

public static URL[] buildProjectClasspathList(ArtifactReference artifactReference)  throws ArtifactResolutionException, MalformedURLException, DependencyResolutionRequiredException {

    List<URL> projectClasspathList = new ArrayList<URL>();

    // Load build class path
    if(classpathElementsCache == null) {
        Application.getLogger().debug("Compile Classpath Elements Cache was null; fetching update");
        classpathElementsCache = artifactReference.getMavenProject().getCompileClasspathElements();
    }

    for (String element : classpathElementsCache) {
        Application.getLogger().debug("Looking at compile classpath element (via MavenProject): " + element);
        Application.getLogger().debug("  Adding: " + element);
        projectClasspathList.add(new File(element).toURI().toURL());
    }

    // Load artifact(s) jars using resolver
    if(dependencyArtifactsCache == null) {
        Application.getLogger().debug("Dependency Artifacts Cache was null; fetching update");
        dependencyArtifactsCache =  artifactReference.getMavenProject().getDependencyArtifacts();
    }

    Application.getLogger().debug("Number of artifacts to resolve: "
            + dependencyArtifactsCache.size());

    for (Artifact unresolvedArtifact : dependencyArtifactsCache) {
        String artifactId = unresolvedArtifact.getArtifactId();

        if (!isArtifactResolutionRequired(unresolvedArtifact, artifactReference)) {
            Application.getLogger().debug("  Skipping: " + unresolvedArtifact.toString());
            continue;
        }

        org.eclipse.aether.artifact.Artifact aetherArtifact = new DefaultArtifact(
                unresolvedArtifact.getGroupId(),
                unresolvedArtifact.getArtifactId(),
                unresolvedArtifact.getClassifier(),
                unresolvedArtifact.getType(),
                unresolvedArtifact.getVersion());

        ArtifactRequest artifactRequest = new ArtifactRequest()
                .setRepositories(artifactReference.getRepositories())
                .setArtifact(aetherArtifact);

        // This takes time; minimizing what needs to be resolved is the goal of the specified dependency code block
        ArtifactResult resolutionResult = artifactReference.getRepoSystem()
                .resolveArtifact(artifactReference.getRepoSession(), artifactRequest);

        // The file should exist, but we never know.
        File file = resolutionResult.getArtifact().getFile();
        if (file == null || !file.exists()) {
            Application.getLogger().warn("Artifact " + artifactId +
                    " has no attached file. Its content will not be copied in the target model directory.");
            continue;
        }

        String jarPath = "jar:file:" + file.getAbsolutePath() + "!/";
        Application.getLogger().debug("Adding resolved artifact: " + file.getAbsolutePath());
        projectClasspathList.add(new URL(jarPath));
    }

    return projectClasspathList.toArray(new URL[]{});
}

其中 ArtifactReference 包含 MavenProject 和 Repo 对象:

public class ArtifactReference {
    private MavenProject mavenProject;
    private RepositorySystem repoSystem;
    private RepositorySystemSession repoSession;
    private List<RemoteRepository> repositories;
    private List<String> specifiedDependencies;
}

关于java - Field#getGenericType() 抛出 java.lang.TypeNotPresentException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49184888/

相关文章:

c# - 创建表单实例时的反射错误

Java:转换原始类

java - 编写一个java数组并得到太多输出

java mail - 处理csv附件

java - Kafka Consumer 示例抛出 ClassNotFoundException

java - 如何在使用 maven 构建的 war 中包含系统依赖项

java - 无法解析符号 'WebView'。在 IntelliJ IDEA 中使用 Maven 的 JavaFX WebView

java - org.hibernate.MappingException : Unknown entity:

java - 使用 sshj 的 SFTP 服务器上不受支持的 SETSTAT 请求的解决方法

Java 反射 : wait() method repeat thrice. 为什么?