Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why I can't detect annotations from a loaded java class?

I have a plugin within i want to access to the class list of my model package from my maven project. Until now i just did this to load the classes into the plugin :

try {
            runtimeClasspathElements = project.getRuntimeClasspathElements();

        } catch (DependencyResolutionRequiredException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        URL[] runtimeUrls = new URL[runtimeClasspathElements.size()];
        for (int i = 0; i < runtimeClasspathElements.size(); i++) {
          String element = (String) runtimeClasspathElements.get(i);
          try {
            runtimeUrls[i] = new File(element).toURI().toURL();
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

newLoader = new URLClassLoader(runtimeUrls,
      Thread.currentThread().getContextClassLoader());
      try {          class=newLoader.loadClass("com.pkl.bc.personnaldata.model.Personne");

    if(class!=null)
        System.out.println(class.getCanonicalName());
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

Until here i can see the full name of my class.

System.out.println(class.getDeclaredFields());
System.out.println(class.isAnnotationPresent(EditColumn.class));
for (Field f : class.getDeclaredFields()) {

                EditColumn v = f.getAnnotation(EditColumn.class);
                if (v != null) {

                    System.out.println(v.tableName());
                    System.out.println(v.oldName());

                }

            }

but i don't get anything, here is the output :

[Ljava.lang.reflect.Field;@398f573b
false

I also tried to use refelctions

Reflections reflections = new Reflections("com.pkl.bc.personnaldata.model.Personne");

          Set<Field> annotated = reflections.getFieldsAnnotatedWith(EditColumn.class);

          System.out.println(annotated);

this give me an empty list. here is my annotation :

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface EditColumn {

    String oldName() default "";

    String newName() default "";

    String tableName() default "";
}

the annotated field :

@EditColumn(newName = "main_adress", oldName = "adress", tableName = "Personne")
    private String main_adress;
like image 374
elpazio Avatar asked Oct 28 '25 05:10

elpazio


1 Answers

I fixed my answer,that your problem is loading different class instance in different ClassLoader when Thread.getContextClassLoader() returns null,because URLClassLoader(urls,parent) when parent is null.you can see the tests that both ways java return a Proxy instance for Annotation Name instance.occurs this problem often someone calls Thread.currentThread().setContextClassLoader(null) in somewhere.so you can solve the problem by checking the contextLoader whether is null.for example:

ClassLoader context=Thread.currentThread().getContextClassLoader();
if(context==null){
  context=getClass().getClassLoader();
}
URLClassLoader loader=new URLClassLoader(urls,context);

Test

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;

import static java.lang.String.format;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

/**
 * Created by holi on 3/24/17.
 */
public class AnnotationsTest {
    private interface StubClass {
        Class<?> stubClass() throws ClassNotFoundException;

        default <T extends Annotation> T annotation(Class<T> type) throws Exception {
            return stubClass().getAnnotation(type);
        }

        default Annotation[] annotations() throws Exception {
            return stubClass().getAnnotations();
        }
    }

    private static final StubClass JAR_WITHIN_ANNOTATION_CLASS = jar("stubs-within-annotation-class.jar");
    private static final StubClass JAR_WITHOUT_ANNOTATION_CLASS = jar("stubs-without-annotation-class.jar");

    public static StubClass jar(String jar) {
        URL jarFile = Objects.requireNonNull(ClassLoader.getSystemResource(jar), format("Jar file not found:%s", jar));
        return () -> {
            ClassLoader context = Thread.currentThread().getContextClassLoader();
            return new URLClassLoader(new URL[]{jarFile}, context).loadClass("Stub");
        };
    }

    private ClassLoader original;

    @BeforeEach
    void setUp() throws Throwable {
        original = Thread.currentThread().getContextClassLoader();
    }

    @AfterEach
    void tearDown() throws Throwable {
        Thread.currentThread().setContextClassLoader(original);
    }

    @Test
    void getAnnotationFromJarClassesWillReturnsContextLoaderAnnotationSharedInstanceIfContextLoaderAssociatedWithRuntimeClassLoader() throws Throwable {
        Set<Object> annotationsCreated = new HashSet<>();
        Set<Object> stubClassCreated = new HashSet<>();
        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());

        Stream.of(JAR_WITHIN_ANNOTATION_CLASS, JAR_WITHOUT_ANNOTATION_CLASS).forEach(asserts(stub -> {
            Name it = stub.annotation(Name.class);
            assertThat(it, is(instanceOf(Name.class)));
            assertThat(it.value(), equalTo("stub"));
            annotationsCreated.add(it);
            stubClassCreated.add(stub.stubClass());
        }));

        assertThat(annotationsCreated, hasSize(1));
        assertThat(stubClassCreated, hasSize(2));
    }

    @Test
    void getAnnotationFromJarClassesWillReturnsNullIfNoContextLoaderAssociated() throws Throwable {
        Thread.currentThread().setContextClassLoader(null);

        Stream.of(JAR_WITHIN_ANNOTATION_CLASS, JAR_WITHOUT_ANNOTATION_CLASS).forEach(asserts(it -> {
            //create different class instance in each class loader
            assertThat(it.stubClass().getName(), equalTo("Stub"));

            assertThat(it.annotation(Name.class), is(nullValue()));
        }));

        assertThat(JAR_WITHOUT_ANNOTATION_CLASS.annotations(), is(emptyArray()));
        assertThat(JAR_WITHIN_ANNOTATION_CLASS.annotations(), arrayWithSize(1));
        Annotation it = JAR_WITHIN_ANNOTATION_CLASS.annotations()[0];

        assertThat(it.annotationType(), is(not(instanceOf(Name.class))));
        assertThat(it.annotationType().getName(), equalTo(Name.class.getName()));
        assertThat(it.annotationType().getDeclaredMethod("value").invoke(it), equalTo("stub"));
    }


    private interface Assert<T> {
        void assertThat(T value) throws Exception;
    }

    private <T> Consumer<T> asserts(Assert<T> executor) {
        return (value) -> {
            try {
                executor.assertThat(value);
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                RuntimeException wrappedException = new RuntimeException(e);
                wrappedException.setStackTrace(e.getStackTrace());
                throw wrappedException;
            }
        };
    }
}

@Name Annotation

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Created by holi on 3/24/17.
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface Name {
    String value();
}

Stub class

@Name("stub")
public class Stub{
}
like image 162
holi-java Avatar answered Oct 29 '25 20:10

holi-java



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!