Liigu peamise sisu juurde

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.