I was curious on how annotations are used during runtime, so after some googling I gave it a try, but kinda have the feeling that is not the proper way as I still have to call the annotation helper on the object. Maybe I'm confusing annotations with Typescript decorators where you can have all the code and logic inside the decorator and you'll just have to decorate a class / field etc.
Anyway here's the code and thanks for having a look, any constructive feedback and/or more resources to read more than welcomed.
Edit. Forgot what I wanted to do
Basically was thinking a class that will map to anything outside the program can encrypt a value and when retrieved its encrypted value in another class that value could be decrypted.
package org.example;
import org.example.annotations.implementation.EncryptDecrypt;
import org.example.models.Person;
import org.example.models.PersonDTO;
public class App
{
public static void main( String[] args )
{
PersonDTO pdto = new PersonDTO("[email protected]", "DTO");
EncryptDecrypt.encrypt(pdto);
System.out.println("pdto email: " + pdto.getEmail());
Person p = new Person(pdto.getEmail(), pdto.getName());
EncryptDecrypt.decrypt(p);
System.out.println("person email: " + p.getEmail());
}
}
package org.example.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Decrypt {
}
package org.example.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypt {
}
package org.example.annotations.implementation;
import org.example.annotations.Decrypt;
import org.example.annotations.Encrypt;
import org.example.utils.UselessProtection;
import java.lang.reflect.Field;
import java.util.Arrays;
public class EncryptDecrypt {
public static void encrypt(Object obj) {
if (obj == null) return;
Class<?> cl = obj.getClass();
for (Field field : cl.getDeclaredFields()) {
var annotatedField = field.getAnnotation(Encrypt.class);
if (annotatedField != null) {
field.setAccessible(true);
try {
var methods = obj.getClass().getMethods();
var methodOptional = Arrays.stream(methods).filter(m -> m.getName().equals("getEmail")).findFirst();
var toEncrypt = (String) methodOptional.get().invoke(obj);
field.set(obj, UselessProtection.encrypt(toEncrypt));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void decrypt(Object obj) {
if (obj == null) return;
Class<?> cl = obj.getClass();
for (Field field : cl.getDeclaredFields()) {
var annotatedField = field.getAnnotation(Decrypt.class);
if (annotatedField != null) {
field.setAccessible(true);
try {
var methods = obj.getClass().getMethods();
var methodOptional = Arrays.stream(methods).filter(m -> m.getName().equals("getEmail")).findFirst();
var toDecrypt = (String) methodOptional.get().invoke(obj);
field.set(obj, UselessProtection.decrypt(toDecrypt));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
package org.example.utils;
public class UselessProtection {
public static String encrypt(String input) {
return new StringBuilder(input).reverse().toString();
}
public static String decrypt(String input) {
return encrypt(input);
}
}
Person and PersonDTO are the same, just different classes
package org.example.models;
import org.example.annotations.Encrypt;
public class PersonDTO {
@Encrypt
private final String email;
private final String name;
public PersonDTO(String email, String name) {
this.email = email;
this.name = name;
}
public String getEmail() {
return email;
}
public String getName() {
return name;
}
}
class Person {
// same as PersonDTO apart from
@Decrypt
private final String email;
}