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:
// 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;
}
}
// 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.
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
finalbo'lishi mumkin emas va qayta aniqlanishi mumkin bo'lgan metod hamfinalbo'lishi mumkin emas. -
Yunit-testlash (mashq testlash) klassi uchun
abstractmetodni o'z ichiga oladi, siz o'zingiz subclass yaratishingiz vaabstractmetod 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
@Beanmetodlari 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:
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();
}
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:
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
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:
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract Command createCommand();
}
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:
public class MyValueCalculator {
public String computeValue(String input) {
// ba'zi haqiqiy kod...
}
// boshqa ba'zi metodlar...
}
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:
/**
* 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 ...;
}
}
/**
* 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.
GO TO FULL VERSION