Дар аксари сенарияҳои татбиқ бисёри bin-ҳо дар контейнер ба таври мустақил ҳастанд (singletons). Агар ба bin-и мустақил лозим ояд, ки бо дигар bin-и мустақил ё bin-и номусстакил кор кунад, маъмулан кор бо вобастаҳо дар муайян кардани як bin ҳамчун хосияти дигараш меистад. Проблема пеш меояд, агар давраи ҳаёти bin-ҳо гуногун бошад. Масалан, bin-и мустақили A бояд bin-и номусстакил (prototype) B-ро истифода барад, масалан, ҳар боре ки ба методи дар A муроҷиат мешавад. Контейнер фақат як бор bin-и A-ро сохта, имкони муайян кардани хосиятҳоро фақат як бор медиҳад. Контейнер имконияти додани экземпляри нав аз bin-и B-ро ба bin-и A ҳар боре ки ба он лозим мешавад, надорад.
Ҳалли ин масъала дар он аст, ки аз як дараҷа аз inversion of control даст кашем. Шумо метавонед bin-и A-ро огоҳ кунед дар бораи контейнер, тавассути татбиқи интерфейси ApplicationContextAware
ва иҷрои фармони getBean("B")
барои контейнер, ки ҳар боре ки ба bin-и A лозим аст, як экземпляри (умуман нав)-и bin-и B-ро талаб мекунад. Дар намунаи зерин ин усул нишон дода шудааст:
// класс, который использует класс стиля Command с сохранением состояния для выполнения определенной обработки
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) {
// создаем новый экземпляр соответствующего Command
Command command = createCommand();
// устанавливаем состояние для (как ожидается, совершенно нового) экземпляра Command
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// обратите внимание на зависимость от Spring API!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
// класс, который использует класс стиля Command с сохранением состояния для выполнения определенной обработки
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 {
// создаем новый экземпляр соответствующего Command
val command = createCommand()
// устанавливаем состояние для (как ожидается, совершенно нового) экземпляра Command
command.state = commandState
return command.execute()
}
// обратите внимание на зависимость от Spring API!
protected fun createCommand() =
applicationContext.getBean("command", Command::class.java)
override fun setApplicationContext(applicationContext: ApplicationContext) {
this.applicationContext = applicationContext
}
}
Ин варианти қаблӣ номатлуб аст, зеро коди бизнес (слоя предметной области) огоҳ аз фреймворки Spring аст ва бо он вобаста аст. Внедрение вобастаҳо тавассути метод (Method Injection) - ин як хосияти каме васеъшудаи IoC-контейнерҳои Spring аст, ки имкон медиҳад бо ин ҳолати истифода тоза кор кардан.
Внедрение вобастаҳо тавассути методи ҷустуҷӯ
Внедрение вобастаҳо тавассути методи ҷустуҷӯ - ин имконияти контейнер барои аз нав муайян кардани методҳои bинҳое, ки контейнер идора мекунад ва баргардонидани натиҷаи ҷустуҷӯ барои bин-и дигаре, ки дар контейнер аст. Ҷустуҷӯ одатан bин-и прототипиро пешниҳод мекунад, мисли сенарияе, ки дар фасли қаблӣ тавсиф шудааст. Фреймворки Spring ин внедрение вобастаҳо тавассути методро бо истифода аз тавлиди байт-код тавассути китобхонаи CGLIB барои тавлиди динамикии зерсинф, ки методро аз нав муайян мекунад, амалӣ мекунад.
-
Барои фаъол будани ин тавлиди динамикии зерсинфҳо, класс, ки контейнери bин-и Spring онро зерсинф мекунад, набояд
final
бошад ва методи аз нав муайяншаванда низ набоядfinal
бошад. -
Санҷиши модулӣ (юнит-тестиронӣ) класса, ки дар он методи
abstract
мавҷуд аст, талаб мекунад, ки шумо худатон зерсинфи класса созед ва татбиқи функсияи-заминаиabstract
методро фароҳам кунед. -
Ба ҳамин тариқ, методҳои мушаххас барои скан кардани компонентҳо лозиманд, ки барои онҳо класҳои мушаххас лозиманд.
-
Чизи дигаре, ки бояд дар хотир дошта бошем, ин аст, ки методҳои ҷустуҷӯ бо методҳои фабричӣ ва, дар хусусият, методҳои
@Bean
дар класҳои конфигуратсионӣ кор намекунанд, зеро дар ин ҳолат контейнер барои тавлиди экземпляр масъул нест ва аз ин рӯ наметавонад зерсинфи динамикии тавлиди-онлайниро созад.
Дар мавриди класса CommandManager
дар порчаи кодекси қаблӣ, контейнери Spring динамикӣ татбиқи методи createCommand()
-ро аз нав муайян мекунад. Класси CommandManager
ягон вобастагии Spring надорад, мисли намунаи таҷдидшуда нишон медиҳад:
package fiona.apple;
// больше никакого импорта Spring!
public abstract class CommandManager {
public Object process(Object commandState) {
// создаем новый экземпляр соответствующего интерфейса Command
Command command = createCommand();
// устанавливаем состояние для (как ожидается, совершенно нового) экземпляра Command
command.setState(commandState);
return command.execute();
}
// хорошо... но где реализация этого метода?
protected abstract Command createCommand();
}
package fiona.apple
// больше никакого импорта Spring!
abstract class CommandManager {
fun process(commandState: Any): Any {
// создаем новый экземпляр соответствующего интерфейса Command
val command = createCommand()
// устанавливаем состояние для (как ожидается, совершенно нового) экземпляра Command
command.state = commandState
return command.execute()
}
// хорошо... но где реализация этого метода?
protected abstract fun createCommand(): Command
}
Дар класс-клиенте, ки методро, ки бояд внедрение шавад (дар ин ҳолат CommandManager
), дорад, бояд сигнатураи зеринро дошта бошад:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
Агар методи abstract
бошад, пас зерсинфи динамикӣ тавлидшуда ин методро татбиқ мекунад. Дар акси ҳол, зерсинфи динамикӣ тавлидшуда методи мушаххаси муайяншударо дар класс-и аслӣ аз нав муайян мекунад. Ин мисоли зеринро дида мебароем:
<!-- бин, сохраняющий состояние, развернутый как прототип (не одиночка) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- внедряем зависимости здесь по мере необходимости -->
</bean>
<!-- commandProcessor использует statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
Bине, ки бо номи commandManager
муайян карда шудааст, методи худи createCommand()
-ро ҳар боре ки ба ӯ экземпляри нав аз bин-и myCommand
лозим мешавад, даъват мекунад. Разворачивать бин-и myCommand
ҳамчун прототип бояд бо эҳтиёт бошад, агар ин воқеан лозим бошад. Агар ин бине-усония бошад, пас ҳар бор он ҳамон экземпляри bин-и myCommand
-ро бармегардонад.
Ба ҷои ин, дар модели компоненти асосӣ бар асоси аннотацияҳо, шумо метавонед методи ҷустуҷӯро бо истифода аз аннотацияи @Lookup
эълон кунед, мисли дар намунаи зерин:
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
}
Ё, ки бештар маъмулӣ, шумо метавонед такя кунед ба он, ки bин-и мақсаднок дар мувофиқи навъи эълоншудаи бозгардонии методи ҷустуҷӯ лозим мешавад:
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
}
Таваҷҷӯҳ кунед, ки, маъмулан, бояд ин гуна методҳои ҷустуҷӯ бо аннотация эълоншариппанияшон дошта бошанд, то ки бо қоидаҳои скан кардани компонентҳои Spring мувофиқ бошанд, ки дар он классҳои абстрактӣ одатан нодида гирифта мешаванд. Ин маҳдудият ба классҳое, ки ба таври равшан сабт шуда ва ё ба таври равшан ворид карда шудаанд, татбиқ намешавад.
Усули дигар ҷиҳати дастрасии bинҳои мақсаднок, ки дар доираи гуногун дар дохили гурӯҳи ҷустуҷӯи ObjectFactory
/ Provider
аст. Ба қисмати " Бинҳо, ки дар доираҳо ҳамчун вобастаҳо ҳастанд" нигаред.
Шумо метавонед низ аз ServiceLocatorFactoryBean
(дар баста org.springframework.beans.factory.config
) истифода баред.
Табдили ихтиёрии метод
Як намуди камтар истифода барии внедрение вобастаҳо тавассути метод, нисбат ба внедрение вобастаҳо тавассути методи ҷустуҷӯ, имконияти табдили ихтиёрии методҳо дар bини идорашуда ба татбиқи дигар метод аст. Шумо метавонед бо боварӣ қисми боқимондаро аз ин фасл гузаронед, то замоне шумо ба ин хусусият ниёз дошта бошед.
Бо истифода аз маълумотҳои конфигуратсионии асосӣ дар XML, шумо метавонед унсури replaced-method
-ро барои иваз кардани татбиқи мавҷудаи метод ба татбиқи дигар барои bини декларшуда истифода баред. Дар зер мо классеро мебинем, ки дар он методи computeValue
вуҷуд дорад, ки бояд аз нав муайян шавад:
public class MyValueCalculator {
public String computeValue(String input) {
// немного реального кода...
}
// некоторые другие методы...
}
class MyValueCalculator {
fun computeValue(input: String): String {
// немного реального кода...
}
// некоторые другие методы...
}
Классе, ки интерфейси org.springframework.beans.factory.support.MethodReplacer
-ро татбиқ мекунад, таърифи нави методро таъмин мекунад, мисли дар намунаи зерин нишон дода шудааст:
/**
* предназначен для переопределения существующего computeValue(String).
* реализация в MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// получаем входное значение, работаем с ним и возвращаем вычисленный результат
String input = (String) args[0];
...
return ...;
}
}
/**
* предназначен для переопределения существующего computeValue(String).
* реализация в MyValueCalculator
*/
class ReplacementComputeValue : MethodReplacer {
override fun reimplement(obj: Any, method: Method, args: Array<out Any>): Any {
// получаем входное значение, работаем с ним и возвращаем вычисленный результат
val input = args[0] as String;
...
return ...;
}
}
Таърифи bини барои татбиқи классаи аслӣ ва муайян кардани методи ивазкунанда чунин хоҳад буд:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
<!-- произвольная замена метода -->
<replaced-method name="computeValue" replacer="replacementComputeValue">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
Шумо метавонед як ё якчанд унсурҳои <arg-type/>
дар дохили унсури <replaced-method/>
барои муайян кардани сигнатураи метод барои методи ивазшаванда истифода баред. Сигнатура барои аргументҳо танҳо дар ҳолате лозим аст, ки агар методи боргузорӣ шуда бошад ва дар класс якчанд вариант вуҷуд дошта бошад. Барои осонӣ, сатри навъи аргумент метавонад як қисми пурраи номи навъи бошад. Масалан, ҳама зерин ба java.lang.String
мувофиқ аст:
java.lang.String
String
Str
Азбаски шумораи аргументҳо одатан барои фарқ кардани ҳар як варианти эҳтимолӣ кофӣ аст, ин ихтисорот метавонад вақтро ба таври зиёд сарфа кунад, ки бо додани фақат сатри кӯтоҳтарин ба навъи аргумент мувофиқ бошад.
GO TO FULL VERSION