CodeGym /Kurslar /JAVA 25 SELF /Refleksiya və Dependency Injection

Refleksiya və Dependency Injection

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

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 JacksonGson 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 FieldMethod 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.

1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Refleksiya
Refleksiya və dinamik imkanlar
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION