JavaRush /Курсхо /All lectures for TG purposes /Внедрение вобастаҳо тавассути метод

Внедрение вобастаҳо тавассути метод

All lectures for TG purposes
Сатҳи , Дарс
дастрас

Дар аксари сенарияҳои татбиқ бисёри 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-ро талаб мекунад. Дар намунаи зерин ин усул нишон дода шудааст:

Java
// класс, который использует класс стиля 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;
    }
}
Kotlin
// класс, который использует класс стиля 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 надорад, мисли намунаи таҷдидшуда нишон медиҳад:

Java
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();
}
Kotlin
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 эълон кунед, мисли дар намунаи зерин:

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
}

Ё, ки бештар маъмулӣ, шумо метавонед такя кунед ба он, ки bин-и мақсаднок дар мувофиқи навъи эълоншудаи бозгардонии методи ҷустуҷӯ лозим мешавад:

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
}

Таваҷҷӯҳ кунед, ки, маъмулан, бояд ин гуна методҳои ҷустуҷӯ бо аннотация эълоншариппанияшон дошта бошанд, то ки бо қоидаҳои скан кардани компонентҳои Spring мувофиқ бошанд, ки дар он классҳои абстрактӣ одатан нодида гирифта мешаванд. Ин маҳдудият ба классҳое, ки ба таври равшан сабт шуда ва ё ба таври равшан ворид карда шудаанд, татбиқ намешавад.

Усули дигар ҷиҳати дастрасии bинҳои мақсаднок, ки дар доираи гуногун дар дохили гурӯҳи ҷустуҷӯи ObjectFactory / Provider аст. Ба қисмати " Бинҳо, ки дар доираҳо ҳамчун вобастаҳо ҳастанд" нигаред.

Шумо метавонед низ аз ServiceLocatorFactoryBean (дар баста org.springframework.beans.factory.config) истифода баред.

Табдили ихтиёрии метод

Як намуди камтар истифода барии внедрение вобастаҳо тавассути метод, нисбат ба внедрение вобастаҳо тавассути методи ҷустуҷӯ, имконияти табдили ихтиёрии методҳо дар bини идорашуда ба татбиқи дигар метод аст. Шумо метавонед бо боварӣ қисми боқимондаро аз ин фасл гузаронед, то замоне шумо ба ин хусусият ниёз дошта бошед.

Бо истифода аз маълумотҳои конфигуратсионии асосӣ дар XML, шумо метавонед унсури replaced-method-ро барои иваз кардани татбиқи мавҷудаи метод ба татбиқи дигар барои bини декларшуда истифода баред. Дар зер мо классеро мебинем, ки дар он методи computeValue вуҷуд дорад, ки бояд аз нав муайян шавад:

Java
public class MyValueCalculator {
    public String computeValue(String input) {
        // немного реального кода...
    }
    // некоторые другие методы...
}
Kotlin
class MyValueCalculator {
    fun computeValue(input: String): String {
        // немного реального кода...
    }
    // некоторые другие методы...
}

Классе, ки интерфейси org.springframework.beans.factory.support.MethodReplacer-ро татбиқ мекунад, таърифи нави методро таъмин мекунад, мисли дар намунаи зерин нишон дода шудааст:

Java
/**
 * предназначен для переопределения существующего 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 ...;
    }
}
Kotlin
/**
 * предназначен для переопределения существующего 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

Азбаски шумораи аргументҳо одатан барои фарқ кардани ҳар як варианти эҳтимолӣ кофӣ аст, ин ихтисорот метавонад вақтро ба таври зиёд сарфа кунад, ки бо додани фақат сатри кӯтоҳтарин ба навъи аргумент мувофиқ бошад.

Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION