Proksi mexanizmlari

All lectures for UZ purposes
Daraja, Dars
Mavjud

Spring AOP biron-bir maqsadli ob'ekt uchun proksi yaratish uchun JDK dinamik proksilari yoki CGLIB ishlatadi. JDK dinamik proksilari JDK’ga qilingan, CGLIB esa ochiq manbali oddiy class yaratish kutubxonasi (spring-core ichida qayta qadoqlangan).

Agar proksilangan maqsadli ob'ekt kamida bitta interfeysni amalga oshirsa, JDK dinamik proksi ishlatiladi. Maqsadli tur amalga oshirgan barcha interfeyslar proksilanadi. Agar maqsadli ob'ekt hech qanday interfeysni amalga oshirmasa, CGLIB-proksi yaratiladi.

Agar CGLIB-proksilashni majburlash kerak bo'lsa (masalan, maqsadli ob'ekt uchun aniqlangan har bir metodni, faqat u amalga oshirgan interfeyslarini emas, proksilash), bu ham amalga oshirilishi mumkin. Biroq, quyidagi muammolarni ko'rib chiqish kerak:

  • CGLIB holatida final metodlar maslahatlar bilan ta'minlanishi mumkin emas, chunki ular ishlash vaqtida yaratilgan subklasslarda qayta aniqlanishi mumkin emas.

  • Spring 4.0 dan boshlab, proksilangan ob'ekt konstruktori endi ikki marta chaqirilmaydi, chunki CGLIB-proksi Objenesis orqali yaratiladi. Agar sizning JVM konstruktordan chetlab o'tishga ruxsat bermasa, Spring’ning AOP qo‘llab-quvvatlovchi vositalaridan ikki marta chaqirishlar va mos keluvchi jurnal yozuvlarini ko‘rishingiz mumkin.

CGLIB-proksini majburiy ishlatish uchun, <aop:config> elementining proxy-target-class atributining qiymatini true ga o'rnating, quyida ko'rsatilgandek:

<aop:config proxy-target-class="true">
    <!-- bu yerda boshqa binlar aniqlangan... -->
</aop:config>

@AspectJ dagi avtomatik proksi qo‘llab-quvvatlashidan foydalanib CGLIB-proksilashni majburiy amalga oshirish uchun <aop:aspectj-autoproxy> elementining proxy-target-class atributini true ga o'rnating, quyida ko'rsatilgandek:

<aop:aspectj-autoproxy proxy-target-class="true"/>

Bir qancha <aop:config/> bo'limlari dastur ishlash vaqtida yagona avtomatik proksi yaratish vositasida birlashtiriladi, u eng qattiq proksi sozlamalarini qo'llaydi, har qanday <aop:config/> bo'limlarida ko'rsatilgan (odatda, boshqa XML-bin aniqlash fayllaridan). Bu <tx:annotation-driven/> va <aop:aspectj-autoproxy/> elementlariga ham tegishli.

Ko'proq tushunish uchun, <tx:annotation-driven/>, <aop:aspectj-autoproxy/> yoki <aop:config/> elementlarida proxy-target-class="true" ni ishlatish CGLIB-proksini har uchala elementlar uchun majburiy qo'llaydi.

AOP proksi haqida asosiy ma'lumotlar

Spring AOP proksilarga asoslangan. O'zingizning aspektlaringizni yozishni boshlashdan oldin yoki Spring Framework bilan birga yetkaziladigan Spring AOPga asoslangan har qanday aspektlardan foydalanishdan oldin bu oxirgi tavsiyaning nimani anglatishini tushunishingiz juda muhim.

Avvaliga sizda oddiy, odatiy, proksilanmagan, hech qanday o'ziga xos xususiyatga ega bo'lmagan oddiy ob'ektga havola bor deb tasavvur qiling, quyidagi kod parchasida ko'rsatilgandek:

Java
public class SimplePojo implements Pojo {
    public void foo() {
        // bu yerda methodni chaqirish "this" havolani to'g'ridan-to'g'ri chaqiradi
        this.bar();
    }
    public void bar() {
        // ozgina mantiq...
    }
}
Kotlin
class SimplePojo : Pojo {
    fun foo() {
        // bu yerda methodni chaqirish "this" havolani to'g'ridan-to'g'ri chaqiradi
        this.bar()
    }
    fun bar() {
        // ozgina mantiq...
    }
}

Agar siz ob'ektga havoladan methodni chaqirsangiz, method o'sha ob'ekt havolasi orqali to'g'ridan-to'g'ri chaqiriladi, quyidagi rasm va listingda ko'rsatilgandek:

Java
public class Main {
    public static void main(String[] args) {
        Pojo pojo = new SimplePojo();
        // bu yerda metodni chaqirish "pojo" havolasi orqali to'g'ridan-to'g'ri bajariladi
        pojo.foo();
    }
}
Kotlin
fun main() {
    val pojo = SimplePojo()
    // bu yerda metodni chaqirish "pojo" havolasi orqali to'g'ridan-to'g'ri bajariladi
    pojo.foo()
}

Vaziyat bir oz o'zgaradi, agar koddagi havola proksi bo'lsa. Quyidagi sxema va kod parchasini ko'rib chiqing:

Java
public class Main {
    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());
        Pojo pojo = (Pojo) factory.getProxy();
        // bu proksi uchun metod chaqiruv!
        pojo.foo();
    }
}
Kotlin
fun main() {
    val factory = ProxyFactory(SimplePojo())
    factory.addInterface(Pojo::class.java)
    factory.addAdvice(RetryAdvice())
    val pojo = factory.proxy as Pojo
    // bu proksi uchun metod chaqiruv!
    pojo.foo()
}

Bu yerda eng muhim narsa shundaki, Main klassining main(..) metodidagi klient kodi proksi havolasini o'z ichiga oladi. Bu, ushbu ob'ekt havolasi uchun metod chaqiruvlari proksi chaqiruvi ekanligini anglatadi. Natijada, proksi ushbu aniq metod chaqiruvi bilan bog'liq bo'lgan barcha interceptorlar (maslahatlar) ni delegatsiya qilishi mumkin. Biroq, chaqiruv maqsadli ob'ektga yetib kelgandan so'ng (bizning holatimizda SimplePojo havolasi), u this.bar() yoki this.foo() kabi o'zi uchun amalga oshiriladigan barcha metod chaqiruvlari this havolasi uchun amalga oshiriladi, proksi uchun emas. Bu muhim oqibatlarga ega. Bu o'zini chaqirish, metod chaqiruvi bilan bog'liq maslahatni bajarishga imkon bermasligini anglatadi.

Yaxshi, lekin buni qanday hal qilish mumkin? Eng yaxshi yondashuv (bu erda "eng yaxshi" so'zi nisbatan foydalanilgan) kodingizni o'zini chaqirish sodir bo'lmaydigan tarzda qayta tuzishdir. Bu sizdan bir oz harakat talab qiladi, lekin bu eng yaxshi, eng kam tajovuzkor yondashuv. Quyidagi yondashuv mutlaqo dahshatli bo'lib, biz buni bunchalik dahshatli ekanligi sababli unga e'tibor qaratishimiz kerakmi yoki yo'qmi aniq emas. Siz (biz uchun qanchalik og'riqli bo'lsa ham) o'z klassingizdagi mantig'ni to'liq Spring AOP ga bog'lashingiz mumkin, quyidagi misolda ko'rsatilgandek:

Java
public class SimplePojo implements Pojo {
    public void foo() {
        // bu ishlaydi, lekin... yuck!
        ((Pojo) AopContext.currentProxy()).bar();
    }
    public void bar() {
        // ozgina mantiq...
    }
}
Kotlin
class SimplePojo : Pojo {
    fun foo() {
        // bu ishlaydi, lekin... yuck!
        (AopContext.currentProxy() as Pojo).bar()
    }
    fun bar() {
        // ozgina mantiq...
    }
}

Bu yondashuv sizning kodingizni butunlay Spring AOP ga bog'laydi, va klass o'zi AOP kontekstida ishlatilayotganini biladi, bu esa AOP ga qarshi keladi. Shuningdek, bu proksi yaratishda qaysidir qo'shimcha sozlashlar talab qiladi, quyidagi misolda ko'rsatilgandek:

Java
public class Main {
    public static void main(String[] args) {
        ProxyFactory factory = new ProxyFactory(new SimplePojo());
        factory.addInterface(Pojo.class);
        factory.addAdvice(new RetryAdvice());
        factory.setExposeProxy(true);
        Pojo pojo = (Pojo) factory.getProxy();
        // bu proksi uchun metod chaqiruv!
        pojo.foo();
    }
}
Kotlin
fun main() {
    val factory = ProxyFactory(SimplePojo())
    factory.addInterface(Pojo::class.java)
    factory.addAdvice(RetryAdvice())
    factory.isExposeProxy = true
    val pojo = factory.proxy as Pojo
    // bu proksi uchun metod chaqiruv!
    pojo.foo()
}

Oxirida, ta'kidlash joizki, AspectJ o‘zini chaqirish muammosiga ega emas, chunki u proksi asosidagi AOP freymvorki emas.

@AspectJ proksilarini dasturiy yaratish

<aop:config> yoki <aop:aspectj-autoproxy> yordamida konfiguratsiyadagi aspektlarni e'lon qilishdan tashqari, maqsadli ob'ektlarga maslahatlar beruvchi proksilarni dasturiy yaratish mumkin. Bu qismda biz @AspectJ aspektlari yordamida avtomatik proksi yaratish imkoniyatiga e'tibor qaratmoqchimiz.

Siz org.springframework.aop.aspectj.annotation.AspectJProxyFactory klassini foydalanib, @AspectJ aspektlari orqali maslahat bilan ta'minlangan maqsadli ob'ekt uchun proksi yaratishingiz mumkin. Ushbu klassdan asosiy foydalanish quyidagi misolda ko'rsatilganidek, juda oddiy ko'rinadi:

Java
// maqsadli ob'ekt uchun proksi yaratishi mumkin bo'lgan fabrikani yaratamiz
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// aspekt qo'shamiz, klass @AspectJ aspekti bo'lishi kerak
// kerakli miqdorda turli aspektlar bilan chaqirish mumkin
factory.addAspect(SecurityManager.class);
// mavjud aspekt namunalarini ham qo'shish mumkin, taqdim etilgan ob'ekt turi @AspectJ aspekti bo'lishi kerak
factory.addAspect(usageTracker);
// endi proxy ob'ektini olamiz...
MyInterfaceType proxy = factory.getProxy();
Kotlin
// maqsadli ob'ekt uchun proksi yaratishi mumkin bo'lgan fabrikani yaratamiz
val factory = AspectJProxyFactory(targetObject)
// aspekt qo'shamiz, klass @AspectJ aspekti bo'lishi kerak
// kerakli miqdorda turli aspektlar bilan chaqirish mumkin
factory.addAspect(SecurityManager::class.java)
// mavjud aspekt namunalarini ham qo'shish mumkin, taqdim etilgan ob'ekt turi @AspectJ aspekti bo'lishi kerak
factory.addAspect(usageTracker)
// endi proxy ob'ektini olamiz...
val proxy = factory.getProxy<Any>()
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION