Why can't I detect annotations from the loaded Java class?
I have a plugin inside. I want to access the class list of my model package from my maven project. So far, 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();
}
So far, 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 get nothing, here is the output:
[Ljava.lang.reflect.Field;@398f573b
false
I have also tried using refelctions
Reflections reflections = new Reflections("com.pkl.bc.personnaldata.model.Personne");
Set<Field> annotated = reflections.getFieldsAnnotatedWith(EditColumn.class);
System.out.println(annotated);
this gives 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 "";
}
annotated field:
@EditColumn(newName = "main_adress", oldName = "adress", tableName = "Personne")
private String main_adress;
source to share
You already retrieve the class with its annotations, but you just need to add a loop to iterate over all the annotations for every field you have.
try this example, it will print annotation name and its values ββwhen available for loaded class field.
for (Field f : loadedClass.getDeclaredFields()) {
System.out.println(f.getName());
for (Annotation a : f.getAnnotations()) {
System.out.println("## SHOWING ANNOTATION FOR FIELD:" + f.getName());
System.out.println(a.toString());
}
}
You can parse toString to get the values ββin this annotation. Waiting for feedback.
source to share
I corrected my answer that your problem is loading an instance of another class into another class ClassLoader when it Thread.getContextClassLoader()
returns null
, because URLClassLoader (urls, parent) when the parent is null. You can see tests that both paths return java a Proxy
instance for Name
instance.occurs annotation this problem is often caused by Thread.currentThread().setContextClassLoader(null)
someone somewhere. You can solve the problem by checking the contextLoader if it 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{
}
source to share