1. Refleksiya ilə seriyalaşdırma
Artıq bilirsiniz ki, seriyalaşdırma — obyektin bayt axınına və ya mətn təsvirinə çevrilməsi prosesidir. Java-da standart seriyalaşdırma (Serializable) var, lakin daha çox Jackson və Gson kimi kitabxanalar vasitəsilə JSON/XML formatlarından istifadə olunur.
Burada refleksiya niyə lazımdır?
Obyekti seriyalaşdırmaq üçün onun sahələrini və dəyərlərini bilmək lazımdır. Sahələr private ola bilər və adi kod onlara çata bilməz — refleksiya isə bacarar. Buna görə JSON kitabxanaları obyektin strukturunu dinamik şəkildə gəzərək, sahələri Field və Method vasitəsilə oxuyur/yazır.
Nümunə: obyektin sətirə sadə seriyalaşdırılması
Məlumat sinfi:
public class Person {
private String name;
private int age;
private boolean active;
public Person(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
}
Refleksiyadan istifadə edən ən sadə serializer:
import java.lang.reflect.Field;
public class SimpleSerializer {
public static String serialize(Object obj) {
StringBuilder sb = new StringBuilder();
Class<?> clazz = obj.getClass();
sb.append(clazz.getSimpleName()).append("{");
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
field.setAccessible(true); // private sahələri açırıq
try {
sb.append(field.getName()).append("=")
.append(field.get(obj));
} catch (IllegalAccessException e) {
sb.append(field.getName()).append("=<?>");
}
if (i < fields.length - 1) sb.append(", ");
}
sb.append("}");
return sb.toString();
}
}
İstifadə:
Person p = new Person("Alice", 30, true);
System.out.println(SimpleSerializer.serialize(p));
Person{name=Alice, age=30, active=true}
Bu necə işləyir?
- getDeclaredFields() vasitəsilə elan edilmiş sahələri alırıq.
- setAccessible(true) vasitəsilə onları əlçatan edirik.
- Sahələrin adlarını və dəyərlərini oxuyub sətir formalaşdırırıq.
Nümunənin məhdudiyyətləri: daxili obyektlər, kolleksiyalar, massivlər və dövri istinadlar işlənmir — bu sadəcə prinsipin nümayişidir.
Tam funksional kitabxanalar bunu necə edir?
Jackson/Gson daxili obyektlər və kolleksiyalarla işləyir, annotasiyaları (@JsonIgnore, @SerializedName), tarix formatlarını və daha çoxunu nəzərə alır — bunların hamısı refleksiya əsasında.
Jackson ilə nümunə:
import com.fasterxml.jackson.databind.ObjectMapper;
Person p = new Person("Bob", 25, false);
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(p);
// {"name":"Bob","age":25,"active":false}
2. Dependency Injection (DI) və refleksiya
Dependency Injection — asılılıqların sinif daxilində yaradılmayıb kənardan “yeridildiyi” nümunə (pattern)dir. Bu, kodu çevik, test edilə bilən və genişləndirilə bilən edir. Java-da bunu freymvörklər Spring, Guice, Dagger @Autowired, @Inject kimi annotasiyalarla yeridilmə nöqtələrini işarələyərək həyata keçirir.
Burada refleksiya niyə lazımdır?
DI-konteyneri sahələri/konstruktorları tapmalı, annotasiyaları oxumalı və instansları runtime-da yaratmalıdır — bu, Class/Constructor/Field API-si vasitəsilə edilir.
Nümunə: refleksiya ilə mini DI
Yeridilmə annotasiyası:
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject {
}
Asılılıqlı siniflər:
public class Service {
public void doWork() {
System.out.println("Service is working!");
}
}
public class Client {
@Inject
private Service service;
public void useService() {
service.doWork();
}
}
Mini konteyner:
import java.lang.reflect.*;
public class MiniDIContainer {
// Sinifə görə obyekt yaradır və @Inject ilə işarələnmiş sahələrə asılılıqları yeridir
public static Object createObject(Class<?> clazz) throws Exception {
Object obj = clazz.getDeclaredConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
Object dependency = createObject(field.getType()); // rekursiv olaraq
field.setAccessible(true);
field.set(obj, dependency);
}
}
return obj;
}
}
İstifadə:
public class Main {
public static void main(String[] args) throws Exception {
Client client = (Client) MiniDIContainer.createObject(Client.class);
client.useService(); // Service is working!
}
}
Bu necə işləyir? Konteyner @Inject olan sahələri tapır, onların tipinə görə asılılıqları yaradır və refleksiya vasitəsilə onları private sahələrə yerləşdirir.
Vacibdir: bu sadələşdirilmiş sxemdir. Real DI-konteynerlər scope-lar, singleton-lar, konfiqurasiya, proksi, dövri asılılıqların işlənməsi və s. dəstəkləyir.
3. Dinamik proksilər
Proksi — çağırışları ələ keçirib əlavə davranış qatan “əvəzedici” obyekt: loqlama, təhlükəsizlik, tranzaksiyalar və s. Java-da bunu java.lang.reflect.Proxy InvocationHandler ilə birlikdə edir. Spring AOP imkanlarının, Mockito-da mokların və s.-nin əsasını təşkil edir.
Nümunə: loqlama edən proksi
public interface HelloService {
void sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
import java.lang.reflect.*;
public class LoggingProxy {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target, Class<T> iface) {
return (T) Proxy.newProxyInstance(
iface.getClassLoader(),
new Class<?>[]{iface},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Metod çağırışı: " + method.getName());
return method.invoke(target, args);
}
}
);
}
}
İstifadə:
HelloService original = new HelloServiceImpl();
HelloService proxy = LoggingProxy.createProxy(original, HelloService.class);
proxy.sayHello("World");
Metod çağırışı: sayHello
Hello, World!
Bu necə işləyir? Proxy.newProxyInstance interfeysi həyata keçirən obyekt yaradır, bütün metod çağırışları isə InvocationHandler.invoke-ə yönləndirilir; burada siz deleqasiyadan əvvəl/sonra istənilən “kəsiyə aid” kodu icra edə bilərsiniz.
4. Real ssenarilər və məhdudiyyətlər
Refleksiya praktikada harada istifadə olunur?
- JUnit — @Test olan metodları tapır və onları refleksiya vasitəsilə çağırır.
- Spring — bin-lər yaradır, asılılıqları yeridir, annotasiyaları skan edir, proksilər generasiya edir.
- Jackson/Gson — hətta private sahələri oxuyaraq seriyalaşdırma/deseriyalaşdırma edir.
- Hibernate — siniflərin strukturuna görə ORM modelləri qurur, sahələri və proksi obyektləri idarə edir.
- Mockito — moklar yaradır və çağırışları proksi vasitəsilə ələ keçirir.
Niyə refleksiyadan həmişə istifadə etmək lazım deyil?
- Performans. Ümumiyyətlə adi çağırışlardan daha yavaşdır (keşləmə/bayt-kod generasiyası ilə yumşaldıla bilər).
- Təhlükəsizlik. İnkapsulyasiyanı pozur; private verilənlərə çıxış verir.
- Java 9+ modulluğu. Paketlərin/modulların açıq elan edilməməsi halında InaccessibleObjectException yarana bilər.
5. Tipik səhvlər
Xəta № 1: checked istisnaların görməzlikdən gəlinməsi. Refleksiya metodları NoSuchFieldException, IllegalAccessException, InvocationTargetException və s. atır. Onları emal edin və ya öz istisnalarınıza qaplayın.
Xəta № 2: setAccessible(true) həmişə işləmir. Java 9+ modullu tətbiqlərdə mümkün InaccessibleObjectException var. JVM/modul parametrləri (--add-opens) və ya açıq API-lər tələb olunur.
Xəta № 3: DI-də dövri asılılıqlar. Sadə rekursiyada (A B-dən, B isə A-dan asılı olduqda) StackOverflowError alacaqsınız. Real konteynerlər asılılıq qrafını izləyir və dövrləri xüsusi texnikalarla həll edirlər.
Xəta № 4: Obyektlərin natamam seriyalaşdırılması. Rekursiv keçid olmadıqda istinad sahələri ClassName@hash kimi seriyalaşdırılır. Düzgün seriyalaşdırma üçün daxili obyektlər/kolleksiyaların emalı və dövrlərə qarşı qorunma lazımdır.
Xəta № 5: Performans itkisi. Tez-tez refleksiv əməliyyatlar (döngələrdə, isti yollarda) dar boğaza çevrilir. Field/Method üçün keşləmədən, bayt-kod generasiyasından, MethodHandle-dan və ya annotation processing-dən istifadə edin.
Xəta № 6: İnkapsulyasiyanın pozulması. Private sahələri refleksiya ilə dəyişmək kövrəkliyə və çətin izlənən xətalara səbəb olur. İctimai (public) müqavilələri üstün tutmağa çalışın.
GO TO FULL VERSION