สร้าง Custom Annotation แบบง่ายๆ
ถ้ากล่าวแบบบ้าน ๆ Annotation ใน Java คือการ inject information บางอย่างเข้าไปให้ Code ของเรารุ้จัก บทความนี้ผมจะพยายามอธิบายแบบบ้านๆ ตัวอย่างแบบบ้านๆ เพื่อที่จะได้นำไปประยุกต์ใช้กันได้นะครับ
- Class Level
ตัวอย่างง่าย ๆ นะครับ เราสร้าง Abstrat Calss AProvince ซึ่งมี Attribute provinces ที่เก็บ List ของจังหวัด (pronvinces) ต่างๆ เอาไว้ ซึ่งเราจะทำให้ Abstract Class นี้สามารถ init ค่า จังหวัดต่างๆ ด้วยการระบุ Annotation @DefaultProvinceAnnotation
ซึ่งเราจะจะ สร้าง Annotation ระดับ class ดังนี้
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(value= RetentionPolicy.RUNTIME)
public @interface DefaultProvinceAnnotation {
String[] provinces();
แล้วเรา ก็ implement getProvince โดยถ้ามีการ ระบุ annotation เข้ามาจะมีการเอาค่าเข้าไป set ให้กับ provinces ตอน Constrator ทำงานดังนี้ครับ
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
public class AProvince {
private List<String> provinces = new ArrayList<>();
public AProvince() {
boolean present = this.getClass()
.isAnnotationPresent(DefaultProvinceAnnotation.class);
if (present) {
DefaultProvinceAnnotation defaultProvinceAnnotation
= this.getClass().getAnnotation(DefaultProvinceAnnotation.class);
if (defaultProvinceAnnotation != null) {
for (String value : defaultProvinceAnnotation.provinces()) {
provinces.add(value);
}
}
}
}
}
ต่อไปเราสร้าง ThailandProvince และ JapanProvince ที่ implement AProvince ดังนี้นะครับ
@DefaultProvinceAnnotation(provinces = {"Bangkok", "Chiangmai", "Chiangrai", "Phayao"})
public class ThailandProvinces extends AProvince {
}
และ
@DefaultProvinceAnnotation(provinces = {"Tokyo", "Osaka"})
public class JapanProvinces extends AProvince {
}
เราสร้าง TestApplication.java ขึ้นมาทดสอบคลาสทั้งสองว่าทำงานได้ถูกต้องหรือไม่
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TestApplication {
public static void main(String[] args) {
ThailandProvinces thailandProvinces =
new ThailandProvinces();
log.info("====== Thailand ======");
thailandProvinces.getProvinces()
.forEach(province -> {
log.info("--> " + province);
});
JapanProvinces japanProvinces =
new JapanProvinces();
log.info("======= Japan =======");
japanProvinces.getProvinces()
.forEach(province -> {
log.info("--> " + province);
});
}
}
จะได้ผลลัพธ์
2. Field Level
ต่อไปเรามาเพิ่ม attribute “continent” ให้กับ ThailandProvince กัน โดยจะใช้ annotaion @DefaultContinent เพื่อกำหนดค่าเริ่มต้นของ ทวีป
import lombok.Getter;
import lombok.Setter;
@DefaultProvinceAnnotation(provinces = {"Bangkok", "Chiangmai", "Chiangrai", "Phayao"})
@Getter
@Setter
public class ThailandProvinces extends AProvince {
@DefaultContinent(continent = "ASIA")
private String continent;
}import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field;
@DefaultProvinceAnnotation(provinces = {"Bangkok", "Chiangmai", "Chiangrai", "Phayao"})
@Getter
@Setter
public class ThailandProvinces extends AProvince {
@DefaultContinent(continent = "ASIA")
private String continent;
public ThailandProvinces() throws IllegalAccessException {
Class<?> clazz = this.getClass();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(DefaultContinent.class)) {
DefaultContinent defaultContinent = field.getAnnotation(DefaultContinent.class);
field.setAccessible(true);
field.set(this, defaultContinent.continent());
}
}
}
}
โดย @DefaultContinent เป็นดังนี้
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 DefaultContinent {
public String continent() default "EUROPE";
}
ต่อมาแก้ไข TestApplication เพื่อแสดงค่า ทวีปของ Thailand
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TestApplication {
public static void main(String[] args) throws IllegalAccessException {
ThailandProvinces thailandProvinces =
new ThailandProvinces();
log.info("====== Thailand ======" + thailandProvinces.getContinent());
thailandProvinces.getProvinces()
.forEach(province -> {
log.info("--> " + province);
});
}
}
เราก็จะได้ผลลัพท์ดังนี้
ซึ่งถ้าเราไม่ระบุ annotaion
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field;
@DefaultProvinceAnnotation(provinces = {"Bangkok", "Chiangmai", "Chiangrai", "Phayao"})
@Getter
@Setter
public class ThailandProvinces extends AProvince {
private String continent;
public ThailandProvinces() throws IllegalAccessException {
Class<?> clazz = this.getClass();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(DefaultContinent.class)) {
DefaultContinent defaultContinent = field.getAnnotation(DefaultContinent.class);
field.setAccessible(true);
field.set(this, defaultContinent.continent());
}
}
}
}
จะได้ผลลัพท์ดังนี้
และท่าระบุแต่ไม่ระบุค่าก็จะได้ค่า default = “EUROPE” ดังนี้
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field;
@DefaultProvinceAnnotation(provinces = {"Bangkok", "Chiangmai", "Chiangrai", "Phayao"})
@Getter
@Setter
public class ThailandProvinces extends AProvince {
@DefaultContinent
private String continent;
public ThailandProvinces() throws IllegalAccessException {
Class<?> clazz = this.getClass();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(DefaultContinent.class)) {
DefaultContinent defaultContinent = field.getAnnotation(DefaultContinent.class);
field.setAccessible(true);
field.set(this, defaultContinent.continent());
}
}
}
}
ผลลัพท์ที่ได้จะเป็นค่า default
3. Method Level
สร้าง annotation ที่ชื่อว่า InitProvince ขึ้นมาครับ
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.METHOD)
public @interface InitProvince {
}
ตอนนี้เราจะเพิ่ม attribut “specialInfomation” ให้กับ class ThailandProvince
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@DefaultProvinceAnnotation(provinces = {"Bangkok", "Chiangmai", "Chiangrai", "Phayao"})
@Getter
@Setter
public class ThailandProvinces extends AProvince {
@DefaultContinent
private String continent;
private String specialInformation = "NA";
public ThailandProvinces() throws IllegalAccessException {
Class<?> clazz = this.getClass();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(DefaultContinent.class)) {
DefaultContinent defaultContinent = field.getAnnotation(DefaultContinent.class);
field.setAccessible(true);
field.set(this, defaultContinent.continent());
}
}
}
}
และแก้
@Slf4j
public class TestApplication {
public static void main(String[] args) throws IllegalAccessException {
ThailandProvinces thailandProvinces =
new ThailandProvinces();
log.info("Thailand is in : " + thailandProvinces.getContinent());
log.info("Information : " + thailandProvinces.getSpecialInformation());
thailandProvinces.getProvinces()
.forEach(province -> {
log.info("--> " + province);
});
}
}
เมื่อรันจะได้ผลลัพธ์
เช็คว่า ถ้า method ไหนมีการใช้ @InitProvince เราจะมีการเรียก method นั้นทำงาน ซึ่งเราจะให้ method นั้น set ค่าให้กับ specialInformation นะครับ (จริงๆ เราสามารถกำหนด specialInformation ได้ตั้งแต่ class level แล้วนะครับแต่นี่ผมแค่ต้องการว่า method level ทำงานได้อย่างไร)
import lombok.Getter;
import lombok.Setter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@DefaultProvinceAnnotation(provinces = {"Bangkok", "Chiangmai", "Chiangrai", "Phayao"})
@Getter
@Setter
public class ThailandProvinces extends AProvince {
@DefaultContinent
private String continent;
private String specialInformation = "NA";
@InitProvince
private void initSpecialInformation() {
specialInformation = "Thailand is the land of smile";
}
public ThailandProvinces() throws IllegalAccessException, InvocationTargetException {
Class<?> clazz = this.getClass();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(DefaultContinent.class)) {
DefaultContinent defaultContinent = field.getAnnotation(DefaultContinent.class);
field.setAccessible(true);
field.set(this, defaultContinent.continent());
}
}
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(InitProvince.class)) {
method.setAccessible(true);
method.invoke(this);
}
}
}
}
เราจะได้ผลลัพธ์เป็น
ก็จบแล้วนะครับไม่ยากเลยใช่ไหมครับ เอาไปประยุกต์ใช้ก้นนะครับ