JavaRush /Kurslar /All lectures for UZ purposes /Metod orqali bog'liqlikni kiritish

Metod orqali bog'liqlikni kiritish

All lectures for UZ purposes
Daraja, Dars
Mavjud

Ko'p holatlarda konteynerdagi aksariyat binlar yagona (singletons) bo'ladi. Agar yagona bin boshqa yagona bin bilan yoki yagona bo'lmagan bin bilan ishlashi kerak bo'lsa, odatda bog'liqlik ishini bir binni boshqa binning xususiyati sifatida belgilash orqali bajariladi. Muammo, agar binlarning hayot tsikllari turlicha bo'lsa paydo bo'ladi. Tasavvur qiling, yagona A bin yagona bo'lmagan (prototip) B binidan foydalanishi kerak bo'lsa, masalan, A da har bir metod chaqirilganida. Konteyner yagona A bini yaratadi va shunday qilib, xususiyatlarni o'rnatish imkoniyati faqat bir marta bo'ladi. Konteyner A binga har safar kerak bo'lganda yangi B binini taqdim eta olmaydi.

Yechim nazoratni inversiyalashtirmaslikdan iboratdir. Siz A binga konteynerdan xabardor qilish mumkin, ApplicationContextAware interfeysini amalga oshirish orqali va konteynerdan getBean("B") chaqirishi orqali (odatda, yangi) B bini har safar A binga kerak bo'lganda so'rashi mumkin. Quyidagi misolda ushbu yondashuv ko'rsatilgan:

Java
// holatni saqlaydigan Command uslubidagi klassdan foydalanib, maxsus ishlov berishni bajarish uchun
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CommandManager implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    public Object process(Map commandState) {
        // mos Command ning yangi nusxasini yarating
        Command command = createCommand();
        // holatni (kutish mumkinki, butunlay yangi) Command nusxasiga o'rnatish
        command.setState(commandState);
        return command.execute();
    }
    protected Command createCommand() {
        // Spring API ga bog'liqlikni unutmang!
        return this.applicationContext.getBean("command", Command.class);
    }
    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
Kotlin
// holatni saqlaydigan Command uslubidagi klassdan foydalanib, maxsus ishlov berishni bajarish uchun
package fiona.apple
// Spring-API imports
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
class CommandManager : ApplicationContextAware {
    private lateinit var applicationContext: ApplicationContext
    fun process(commandState: Map<*, *>): Any {
        // mos Command ning yangi nusxasini yarating
        val command = createCommand()
        // holatni (kutish mumkinki, butunlay yangi) Command nusxasiga o'rnatish
        command.state = commandState
        return command.execute()
    }
    // Spring API ga bog'liqlikni unutmang!
    protected fun createCommand() =
            applicationContext.getBean("command", Command::class.java)
    override fun setApplicationContext(applicationContext: ApplicationContext) {
        this.applicationContext = applicationContext
    }
}

Yuqoridagi variant nomaqbul, chunki biznes-kod (mavjud predmet sohasida) Spring freyvmorkdan xabardor va unga bog'langan. Metod orqali bog'liqlikni kiritish (Method Injection) - bu Spring IoC konteynerining bir qadar kengaytirilgan funktsiyasi, bu holatda foydalanish imkonini beradi.

Metod orqali bog'liqlikni kiritish maqsadga muvofiqligi haqida ko'proq bu blog postida o'qishingiz mumkin.

Qidiruv metodi orqali bog'liqlikni kiritish

Qidiruv metodi orqali bog'liqlikni kiritish - bu konteynerning boshqarilayotgan binlarning metodlarini qayta aniqlash va konteynerdagi boshqa nomli bin uchun qidiruv natijasini qaytarish qobiliyati. Qidiruv odatda, oldingi bo'limda tasvirlangan holatda bo'lgani kabi, bin-prototip ko'rinishida bo'ladi. Spring freyvmorki bu bog'liqlikni kiritishni metod orqali bajaradi, bu CGLIB kutubxonasidan bayt-kod yaratishni foydalanib, dinamik ravishda subclass yaratadi, bu metodi qayta aniqlaydi.

  • Bu dinamik subclass yaratish ishlashi uchun, Spring bin konteyneri tomonidan subclass qilingan klass final bo'lishi mumkin emas va qayta aniqlanishi mumkin bo'lgan metod ham final bo'lishi mumkin emas.

  • Yunit-testlash (mashq testlash) klassi uchun abstract metodni o'z ichiga oladi, siz o'zingiz subclass yaratishingiz va abstract metod uchun dummy funktsiyasini amalga oshirishingiz kerak bo'ladi.

  • Shu bilan birga, komponentlarni skanerlash uchun aniq metodlar kerak, ular uchun aniq klasslar zarur.

  • Boshqa muhim cheklovlar shundan iboratki, qidiruv metodlari fabrik metodlar va ayniqsa @Bean metodlari bilan ishlamaydi konfiguratsion klasslarda, chunki bu holatda konteyner instance yaratishga javobgar emas va shuning uchun u "yugurish vaqtida" dinamik ravishda subclass yarata olmaydi.

Oldingi kod parchalaridagi CommandManager klassi holatida Spring konteyner dinamik ravishda createCommand() metodining bajarilishini qayta aniqlaydi. CommandManager klassi Spring bilan hech qanday bog'liqliklarga ega emas, qayta ishlangan misolda ko'rsatilgan:

Java
package fiona.apple;
// endi Spring import yo'q!
public abstract class CommandManager {
    public Object process(Object commandState) {
        // mos Command interfeysining yangi nusxasini yarating
        Command command = createCommand();
        // holatni (kutish mumkinki, butunlay yangi) Command nusxasiga o'rnatish
        command.setState(commandState);
        return command.execute();
    }
    // yaxshi... lekin bu metodning bajaqi qayerda?
    protected abstract Command createCommand();
}
Kotlin
package fiona.apple
// endi Spring import yo'q!
abstract class CommandManager {
    fun process(commandState: Any): Any {
        // mos Command interfeysining yangi nusxasini yarating
        val command = createCommand()
        // holatni (kutish mumkinki, butunlay yangi) Command nusxasiga o'rnatish
        command.state = commandState
        return command.execute()
    }
    // yaxshi... lekin bu metodning bajaqi qayerda?
    protected abstract fun createCommand(): Command
}

Kiritilishi kerak bo'lgan metodni o'z ichiga olgan mijoz klassida (bu holda CommandManager), quyidagi ko'rinishda bo'lishi kerak:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

Agar metod abstract bo'lsa, dinamik ravishda yaratilgan subclass bu metodni amalga oshiradi. Aks holda, dinamik ravishda yaratilgan subclass asl klassda aniqlangan aniq metodni qayta aniqlaydi. Quyidagi misolni ko'rib chiqing:

<!-- holatli bin prototip sifatida o'rnatilgan (yagona emas) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- kerak bo'lganda, bog'liqliklarni bu yerda kiritamiz -->
</bean>
<!-- commandProcessor statefulCommandHelper dan foydalanadi -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

commandManager deb nomlangan bin har safar myCommand bini yangi nusxasiga ehtiyoj sezganda o'zining createCommand() metodini chaqiradi. Prototip sifatida myCommand binini o'rnatish ehtiyotkorlik bilan amalga oshirilishi kerak, agar bu haqiqatan kerak bo'lsa. Agar bu yagona bin bo'lsa, har safar bir xil myCommand binining nusxasi qaytariladi.

Alternativ ravishda, annotation asosidagi komponent modelida qidiruv metodini @Lookup annotatsiyasi yordamida e'lon qilishingiz mumkin, quyidagi misolda ko'rsatilgan:

Java
public abstract class CommandManager {
    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }
    @Lookup("myCommand")
    protected abstract Command createCommand();
}
Kotlin
abstract class CommandManager {
    fun process(commandState: Any): Any {
        val command = createCommand()
        command.state = commandState
        return command.execute()
    }
    @Lookup("myCommand")
    protected abstract fun createCommand(): Command
}

Yoki, eng o'ziga xos usuli, siz qidiruv metodi orqali maqsadli binning hal qilinishiga tayanishingiz mumkin:

Java
public abstract class CommandManager {
    public Object process(Object commandState) {
        Command command = createCommand();
        command.setState(commandState);
        return command.execute();
    }
    @Lookup
    protected abstract Command createCommand();
}
Kotlin
abstract class CommandManager {
    fun process(commandState: Any): Any {
        val command = createCommand()
        command.state = commandState
        return command.execute()
    }
    @Lookup
    protected abstract fun createCommand(): Command
}

Qayd qilish lozimki, bunday an'notatsiyalangan qidiruv metodlarini aniq dummy funksiya amalga oshirilishi bilan e'lon qilish kerak, shunda ular Spring komponentlarini skanerlash qoidalariga mos keladi, chunki abstrakt klasslar odatda e'tiborga olinmaydi. Bu cheklash aniq ro'yxatdan o'tgan yoki aniq import qilingan bin klasslariga tatbiq etilmaydi.

Turli sohalardagi maqsadli binlarga kirishning boshqa usuli ObjectFactory / Provider qo'shimcha nuqtasidir. " Sohadagi bog'liqlar sifatida bo'lgan binlar" bo'limiga qarang.

Sizga ServiceLocatorFactoryBean ( org.springframework.beans.factory.config paketi) ham kerak bo'lishi mumkin.

Ixtiyoriy metodni almashtirish

Qidiruv metodi orqali bog'liqlikni kiritishga qaraganda kamroq foydali shakli ixtiyoriy metodni almashtirishdir. Siz bu bo'lim qolgani o'chirishingiz mumkin, agar bu funksiya sizga kerak emas.

XML asosidagi konfiguratsiya metadatalari yordamida replaced-method elementidan foydalanib, mavjud metodni bajarilishini boshqa metod bajarilishiga almashtirish mumkin. Quyidagi klassni ko'rib chiqing, unda bizga computeValue metodini qayta aniqlash kerak:

Java
public class MyValueCalculator {
    public String computeValue(String input) {
        // ba'zi haqiqiy kod...
    }
    // boshqa ba'zi metodlar...
}
Kotlin
class MyValueCalculator {
    fun computeValue(input: String): String {
        // ba'zi haqiqiy kod...
    }
    // boshqa ba'zi metodlar...
}

Klass org.springframework.beans.factory.support.MethodReplacer interfeysini amalga oshirishi orqali yangi metod aniqlashini beradi, quyidagi misolda ko'rsatilgan:

Java
/**
 * mavjud computeValue(String) ni qayta aniqlash uchun mo'ljallangan.
 * MyValueCalculator da bajarilish
 */
public class ReplacementComputeValue implements MethodReplacer {
    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // kirituvchi qiymatni olamiz, uni ishlaymiz va hisoblajilangan natijani qaytaramiz
        String input = (String) args[0];
        ...
        return ...;
    }
}
Kotlin
/**
 * mavjud computeValue(String) ni qayta aniqlash uchun mo'ljallangan.
 * MyValueCalculator da bajarilish
 */
class ReplacementComputeValue : MethodReplacer {
    override fun reimplement(obj: Any, method: Method, args: Array<out Any>): Any {
        // kirituvchi qiymatni olamiz, uni ishlaymiz va hisoblajilangan natijani qaytaramiz
        val input = args[0] as String;
        ...
        return ...;
    }
}

Original klassni o'rnatish va almashtiriladigan metodni belgilashni amalga oshirish uchun bin aniqlanishi quyidagi ko'rinishda bo'ladi:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- ixtiyoriy metodni almashtirish -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

Siz <replaced-method/> elementi ichida bir yoki bir nechta <arg-type/> elementlaridan foydalanishingiz mumkin, qayta aniqlanishi kerak bo'lgan metod uchun argumentlar tipini belgilashda. Argumentlar uchun imzo faqat metod o'zgaruvchan bo'lganda va klass ichida bir nechta variantlar mavjud bo'lganda zarur. Argument turi uchun satr stringi faqatgina to'liq nomi tipiga mos bo'lishi mumkin. Masalan, quyida keltirilganlarning hammasi java.lang.String ga mos keladi:

java.lang.String
String
Str

Ko'pincha argumentlar soni har bir mumkin variantni ajratib turish uchun etarli bo'lgani sababli, bu qisqartirish faqat argument turi uchun eng qisqa satrni kiritish imkonini beradi va ko'p vaqtni tejashi mumkin.

Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION