Annotatsioonide lugemine
Sissejuhatus
Annotatsioon, mida programm kunagi ei ole, on lihtsalt silt - kasulik dokumentatsioonina, kuid mitte toimiv mehhanism. See, mis muudab annotatsioonid siltidest töötavaks süsteemiks, on reflektsioon: käitusajal küsib raamistik klassilt, meetodilt või väljalt, milliseid annotatsioone need sisaldavad ning tegutseb saadud vastutste põhjal.
See peatükk eeldab, et annotatsioonil on @Retention(RetentionPolicy.RUNTIME) reegel peal.
Ilma selleta ei tagasta ükski allpool näidatud väljakutse midagi.
Annotatsiooni olemasolu kontroll
Kõige esimene samm on kontrollida, kas kontrollitaval elemendil on soovitud annotatsioon olemas:
Method method = SomeClass.class.getDeclaredMethod("loadFromDatabase");
if (method.isAnnotationPresent(Tag.class)) {
System.out.println("Tagged method found");
}
isAnnotationPresent toimib nii Class, Field, Method kui ka Constructor peal ehk kõikidel elementidel, mida saab annotatsioonidega märkida.
Annotatsiooni ja selle parameetrite lugemine
getAnnotation(Class) tagastab annotatsiooni instantsi enda või null, kui annotatsioon puudub:
Tag tag = method.getAnnotation(Tag.class);
if (tag != null) {
for (String name : tag.names()) {
System.out.println("Tagged: " + name);
}
}
tag.names() tagastab väärtused, mis kirjutati kasutuskohas, sealhulgas vaikeväärtused.
Annotatsioonide loetlemine
Kui on vaja loetleda kõik elemendile lisatud annotatsioonid, saab selleks kasutada getAnnotations() meetodit:
for (Annotation a : method.getAnnotations()) {
System.out.println(a);
}
Terve klassi uurimine
Tüüpiline muster, mida raamistikud kasutavad, on kõikide klassi meetodite läbi vaatamine ning nendele reageerimine vastavalt vajadusele:
public static void invokeAudited(Object target) throws Exception {
for (Method method : target.getClass().getDeclaredMethods()) {
if (method.isAnnotationPresent(Audited.class)) {
Audited audited = method.getAnnotation(Audited.class);
System.out.println("Auditing: " + audited.reason());
method.invoke(target);
}
}
}
public class UserActions {
@Audited(reason = "GDPR")
public void deleteUser() { ... }
public void listUsers() { ... } // not annotated, will be skipped
}
invokeAudited(new UserActions()) leiab üles kõik meetodid @Audited annotatsiooniga, loeb nende parameetrid ära ning kutsub need meetodid välja.
Raamistiku kood ei maini kuskil UserActions ega deleteUser klasse või meetodeid.
Iga klass, millel on @Audited annotatsiooniga meetod, töödeldakse samal viisil.
Annotatsioonide lugemine väljadel
Samad väljakutsed toimivad ka Field objektidel.
Toome näiteks eelmise peatüki lõpus tutvustatud @MaxLength annotatsiooni:
public static List<String> validate(Object obj) throws IllegalAccessException {
List<String> errors = new ArrayList<>();
for (Field field : obj.getClass().getDeclaredFields()) {
MaxLength max = field.getAnnotation(MaxLength.class);
if (max != null) { // Has MaxLength annotation
field.setAccessible(true); // Make it "public"
Object value = field.get(obj); // Get the value of field
if (value instanceof String s && s.length() > max.value()) {
errors.add(field.getName() + ": " + max.message());
}
}
}
return errors;
}
Annotatsioonid klassidel
Class<?> võib ka kanda annotatsioone:
@Audited(reason = "all admin operations")
public class AdminConsole {
...
}
Audited classLevel = AdminConsole.class.getAnnotation(Audited.class);
Annotatsioonide pärimine
Vaikimisi ei pärita vanemklassi annotatsioone alamklassidele.
Alamklassi getAnnotation tagastab null, isegi kui vanemklassil oli annotatsioon olemas.
@Inherited meta-annotatsioon muudab seda käitumist, kuid ainult klassitaseme annotatsioonide puhul:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Audited {
String reason();
}
@Inherited ei mõjuta meetodi- ega väljaannotatsioone - neid ei pärita kunagi, olenemata olukorrast.
A complete picture
Kui kõik sammud kokku panna ja järjestada, näeb iga reflektsioonil põhinev annotatsiooniprotsessor enam-vähem ühesugune välja:
Viimane samm erineb - üks raamistik käivitab meetodi, teine salvestab mingid väärtused registrisse, kolmas uurib selle parameetreid ja genereerib koodi. Esimesed neli sammu on aga peaaegu kõikjal identsed.
Järgmises peatükis võtame kõik eelnevalt õpitu kokku läbi praktilise näite.