Instantiate object by it's path got from annotation property - java

1. Have following annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Trackable {
String builder();
}
2. usage of this annotation:
#Trackable(builder = "pkg1.SomeVO")
public class MyService
3. pkg1.SomeVO - is path to the Java object,that should be instantiated further in my aspect class.
4. I've got String value of build,that is equals to 'pkg1.SomeVO' from reflection.
The question is,how actually to instantiate SomeVO object?
I need to it like:
MyBuilder mb=new SomeVO();
where MyBuilder is abstract class,already defined.
It may be any object,e. g. SomeVO2 etc.,so I definitely doesn't know in my aspect(see step 3.),what class should be instantiated.

Do something like this to get the annotation value and create the class. Also, you may want to use the default value field and a Class instead of a String in your annotations to make things easier.
for (Method m : MyService.class.getDelcaredMethods())
if (m.getAnnotation(Trackable.class) != null) {
String className = m.getAnnotation(Trackable.class).builder()
Class.forName(className).newInstance();
}
If your class has no default constructor you need to figure out what args it takes:
for (Constructor c : Class.forName(className).getConstructors())
if (c.getParameterTypes() == /* your expected arg types */)
return c.newInstance(/* your args */);
To ensure your class is of a certain type (e.g. MyBuilder) you can make your annotation be:
public #interface Trackable {
Class<? extends MyBuilder> value()
}

Assuming by path, you mean the package your class is in (which is hopefully on the classpath).
String myClass = getClassNameFromAnnotationReflectively(); // YOU define this method...
Class<SomeVO> someVoClass = Class.forName(myClass);
SomeVO someVo = someVoClass.newInstance();
If your class doesn't have a default constructor, you can find the proper one using getConstructor(Class<?> ...) (where you pass in the types of the arguments of the constructor you are looking for), and then calling newInstance(Object...) on that (passing in the actual values).
There are a lot of fun methods for use on the Class object. I recommend taking a look.

Related

Java Annotation Processing how to check if VariableElement is a primitive type(int, float) or an object some class

I have a class
public class SomeClass {
#CustomAnnotation1
String stringProperty = "LALALA";
#CustomAnnotation1
int integerProperty;
#CustomAnnotation1
ClassObject classObject;
}
CustomAnnotation1 is a custom annotation defined by me which can be put over any Field. Suppose class ClassObject is something like
public class ClassObject {
#CustomAnnotation1
public String someOtherString;
public String log;
}
What I want to achieve - If my annotation is put on any field which is not a primitive type, I want to access all the fields of that class.
My Approach - Get all the fields annotated with CustomAnnotation1, iterate over all of them and if it is non-primitive, get all the fields inside that class and process.
What I've tried - I am able to get all the elements annotated with my annotation using the below code in my AbstractProcessor class.
Collection<? extends Element> annotatedElements = roundEnvironment.getElementsAnnotatedWith(CustomAnnotation1.class);
List<VariableElement> variableElements = ElementFilter.fieldsIn(annotatedElements);
Questions -
I've researched a lot about the VariableElement class but unable to find a way to check if the field is primitive or not. Can this be done?
Is there any better approach to achieve this?
VariableElement.asType().getKind().isPrimitive()

how to read runtime annotations on a field within a class

Imagine an annotation called "MyAnn" with runtime retention, a class MyClass, and an abstract class called MyData. MyClass has a field of type MyData, annotated with MyAnn. Within the instance of MyData, how do see if the annotation MyAnn is present and retrieve its information?
Note - in Java8 I know we can directly annotate the inner class at construction - and that works - but I need this working based on the field annotation.
Thanks for any help!
public MyClass extends MySuperClass() {
#MyAnn(value = "something")
protected MyData mydata;
public void doSomething() {
mydata = new MyData() {};
mydata.initialize();
}
}
public abstract MyData() {
String myValue = null;
public void initialize() {
if (***I am annotated with MyAnn) {
myValue = (***value from annotation MyAnn);
}
}
}
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnn {
String myvalue;
}
MyData cannot directly know whether it has been annotated with MyAnn, because it has no knowledge of whether the current instance is a field or a standalone instance, and if it is a field, then whether the field has or has not been annotated with MyAnn.
So, you will need to somehow obtain and pass the value to initialize(), and you will need to somehow obtain the value at the place where initialize() is called. And from your code, it appears that "something" can be passed as a parameter to initialize(), making the whole thing a lot easier than annotating the field and then checking whether the field is annotated.
But in any case, if you want to check whether a field is annotated, you have to:
obtain the fields of your class with getClass().getDeclaredFields()
loop over the fields; for each field, either
invoke isAnnotationPresent( MyAnn.class ) or
invoke field.getAnnotations(), loop for each annotation, and check whether this annotation instanceof MyAnn.class
Once you have found the annotation, you can get its value with annotation.value();

Calling "super (prototype)" from Java on an open Kotlin data DTO class

I've got a Kotlin class, similar to
data open class MyDto (
var property: String? = null
// ...
)
and a Java class extending this class, similar to
class MySpecificDto extends MyDto {
private String field;
// getter/setter for "field"
public MySpecificDto(final MyDto prototype)
{
super(prototype);
}
}
What is missing in Kotlin's DTO for the "super(prototype)" statement to compile?
MyDto's constructor takes a single parameter of type String, and you are trying to pass it a MyDto.
I think you are looking for --
super(prototype.getProperty());
Data classes seem a like logical base for a hierarchy of DTOs. Unfortunately, they do not play well with inheritance, so doing so is not a good idea. See this answer.
Update to address comment --
For a Kotlin side solution, you need to remember Kotlin classes only allow for a single constructor. For data classes, the format of that constructor is already defined, so you cannot just pass an object and have it work, or define a different constructor. Also, as noted by #bashor in comment to your original question, there is no copy constructor. You can, however, create a separate function to initialize your object if you want --
data open class MyDto (var property: String? = null //...) {
fun init(dto: MyDto) {
property = dto.property
//... rest of the properties
}
}
and the in your Java constructor call init instead of super.
public class MySpecificDto extends MyDto {
private String field;
public MySpecificDto(final MyDto prototype)
{
init(prototype);
}
}
The caveat on this solution is that your data class must provide default values for all of its properties because there is an implicit call to the constructor with zero parameters.

Is there any way to mix or inherit annotation value in Java?

I have an annotation like this
public #interface anno{
String a1() default "defaultValueA1";
String a2() default "defaultValueA2";
String a3() default "defaultValueA3"
}
Class SuperClass{
#anno(a1="myA1", a3="myA3")
private String field1;
}
Class SubClass extends SuperClass(){
#anno(a2="myA2", a3="defaultValueA3")
private String field1;
}
currently when I try to get annotaion from subclass, the anno only contains customized a2, but a1 is only able to get default value, is there any way get an annotation mix all superclass specified fields like {a1:myA1, a2:myA2, a3:defaultValueA3} but not {a1:defaultValueA1,a2:myA2, a3:defaultValueA3}?
update:
I know annotation is not inheritable, so I tried mix subClass annotation {a1:defaultValueA1, a2:myA2, a3:defaultValueA3} and superClass annotaion {a1:myA1, a2: defalutValueA2, a3:myA3}, my way is get all subClass customized values and copy them to superClass annotation, but the problem is when I get all annotation value from subClass, I can't distiguish which value is user defined and which value is come from default value, anyone have suggestion?
private void mixAnnotaiont(Annotation target, Annotation source){
Method[] methods = source.annotationType().getMethods();
Map<String, Object> sourceCfg = AnnotationUtils.getAnnotationAttributes(source);
for (int i = 0; i < methods.length; i++) {
// skip default value, but it's incorrect, user may specify default value in subClass to overwrite superClass value
Object defaultValue = methods[i].getDefaultValue();
String name = methods[i].getName();
if(sourceCfg.get(name).equals(defaultValue)){
ignoreProperties.add(name);
}
}
BeanUtils.copyProperties(source, target, ignoreProperties.toArray(new String[] {}));
}
Thanks for your attention.
Annotations aren't inherited according to JavaDoc:
Open Declaration Annotation[] java.lang.Class.getDeclaredAnnotations()
Returns all annotations that are directly present on this element.
Unlike the other methods in this interface, this method ignores
inherited annotations. (Returns an array of length zero if no
annotations are directly present on this element.) The caller of this
method is free to modify the returned array; it will have no effect on
the arrays returned to other callers.
But you can try have a while loop and get all the declared annotations using this method
java.lang.Class.getSuperclass()
while(loop thru the super class)
{
// get declared annotation
}

Getting object of an annotation on method or constructor parameters in java

I am using Scannotation to scan classfiles and get all classes with annotations present on any element of that class. Using reflection i've been able to find out all annotations on parameters in methods, but i need objects of those annotations so i can later get its parameters (or what do you call it).
this is fraction of my code, which will return annotations i want, but i can't work with them.
public Set<Class> getParametersAnnotatedBy(Class<? extends Annotation> annotation) {
for (String s : annotated) {
//annotated is set containing names of annotated classes
clazz = Class.forName(s);
for (Method m : clazz.getDeclaredMethods()) {
int i = 0;
Class[] params = m.getParameterTypes();
for (Annotation[] ann : m.getParameterAnnotations()) {
for (Annotation a : ann) {
if (annotation.getClass().isInstance(a.getClass())) {
parameters.add(a.getClass());
//here i add annotation to a set
}
}
}
}
}
}
i know i can work with it, if i know the annotation, like this:
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
public String name();
public int count();
}
// ... some code to get annotations
MyAnnotation ann = (MyAnnotation) someAnnotation;
System.out.println(ann.name());
System.out.println(ann.count());
but so far i was not able to do it this way, using reflection... I would very much appreciate any directions, thanks in advance.
PS.: is there any way to get object of parameters like Field for fields, Method for methods etc. ?
You need to use a.annotationType. When you call getClass on an annotation you are actually getting its Proxy Class. To get the real class that it is you need to call annotationType instead of getClass.
if (annotation.getClass() == a.annotationType()) {
parameters.add(a.annotationType());
// here i add annotation to a set
}

Categories