JavaRush /Курстар /All lectures for KK purposes /AspectJ-мен жүктеу уақытындағы байланыстыру Spring-де

AspectJ-мен жүктеу уақытындағы байланыстыру Spring-де

All lectures for KK purposes
Деңгей , Сабақ
Қол жетімді

Жүктеу уақытындағы байланыстыру (Load-time weaving/LTW) Java виртуалды машинасына (Java virtual machin/JVM) кластардың файлдарын жүктеу барысында қолданбаның AspectJ аспектілерін байланыстыру процесіне жатады. Бұл бөлімде көбінесе Spring Framework контекстінде LTW механизмін теңшеу және пайдалану талқыға салынады. Бұл бөлім жүктеу уақытындағы байланыстыру механизміне жалпы кіріспе бола алмайды. Жүктеу уақытындағы байланыстыру механизмінің нақты сипаттамасын және оны тек AspectJ пайдалана отырып теңшеу туралы толық ақпарат алу үшін AspectJ Орта Даму Нұсқаулығындағы жүктеу уақытындағы байланыстыру бөліміне қараңыз.

AspectJ-дің жүктеу уақытындағы байланыстыру механизміне Spring Framework-тің құндылығы, ол байланыстыру процесін әлдеқайда нақтырақ басқаруға мүмкіндік беруінде жатыр. AspectJ-дің ванильді жүктеу уақытындағы байланыстыру механизмі JVM-ді іске қосқанда виртуалды машинаның параметрін орнату арқылы қосылатын Java агентін (5+) қолдану арқылы жүзеге асады. Осылайша, бұл бүкіл JVM үшін параметр болады, ол кейбір жағдайларда жақсы болуы мүмкін, бірақ жиі өте өрескел. Spring-ді қолдау арқылы LTW механизмі оны әр ClassLoader негізінде қосуға мүмкіндік береді, бұл нақтырақ параметр болып табылады және бір JVM-де көптеген қолданбалары бар ортада әлдеқайда тиісті бола алады (әдеттегі қолданба сервері ортасы сияқты).

Сонымен қатар, кейбір орталарда бұл қолдау жүктеу уақытындағы байланыстыруды серверді іске қосу сценарийін өзгертпей жүзеге асыруға мүмкіндік береді, -javaagent:path/to/aspectjweaver.jar немесе (бұл бөлімде одан әрі сипатталғандай) -javaagent:path/to/spring-instrument.jar қосу үшін қажетті жағдайларда. Әзірлеушілер жүктеу уақытындағы байланыстыруды қосу үшін қолданба контекстін орнатады, орнына әдетте орнатуды басқару үшін жауап беретін әкімшілерге сүйенудің орнына.

Біз мақтау сөздерін аяқтасақ, енді Spring-пен AspectJ-мен LTW-дің жылдам мысалын қарастырайық, содан кейін мысалдағы элементтерді тереңірек талдаймыз.

Бірінші мысал

Қолданба әзірлеушісі ретінде сізге кейбір өнімділік мәселелерін диагностикалау тапсырылды деп есептейік. Профилирлеу құралын қолданудың орнына, біз кейбір өнімділік көрсеткіштерін тез алу үшін қарапайым профилирлеу аспектісін қосамыз. Осыдан кейін бұл нақты аймаққа неғұрлым нақтырақ профилирлеу құралын қолдануға болады.

Мұнда ұсынылған мысал XML конфигурациясын пайдаланады. Сіз сондай-ақ Java конфигурациясы арқылы @AspectJ-ді орнатып, пайдалана аласыз. Атап айтқанда, <context:load-time-weaver/> баламасы ретінде @EnableLoadTimeWeaving аннотациясын қолдануға болады.

Келесі мысалда профилирлеудің тым ерекше емес аспектісі көрсетілген. Бұл @AspectJ стиліндегі аспектілерді жариялайтын уақытқа тәуелді профилирлеуші:

Java
package foo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StopWatch;
import org.springframework.core.annotation.Order;
@Aspect
public class ProfilingAspect {
    @Around("methodsToBeProfiled()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch sw = new StopWatch(getClass().getSimpleName());
        try {
            sw.start(pjp.getSignature().getName());
            return pjp.proceed();
        } finally {
            sw.stop();
            System.out.println(sw.prettyPrint());
        }
    }
    @Pointcut("execution(public * foo..*.*(..))")
    public void methodsToBeProfiled(){}
}
Kotlin
package foo
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Pointcut
import org.springframework.util.StopWatch
import org.springframework.core.annotation.Order
@Aspect
class ProfilingAspect {
    @Around("methodsToBeProfiled()")
    fun profile(pjp: ProceedingJoinPoint): Any {
        val sw = StopWatch(javaClass.simpleName)
        try {
            sw.start(pjp.getSignature().getName())
            return pjp.proceed()
        } finally {
            sw.stop()
            println(sw.prettyPrint())
        }
    }
    @Pointcut("execution(public * foo..*.*(..))")
    fun methodsToBeProfiled() {
    }
}

AspectJ байланыстыру құралына біздің ProfilingAspect-ті класстарымызбен байланыстыруды қалайтынымызды хабарлау үшін META-INF/aop.xml файлын жасауымыз қажет. AspectJ стандарты, яғни META-INF/aop.xml атауымен Java кластар жолында файлды (немесе файлдарды) бар болу – файл келісімі. Келесі мысал aop.xml файлын көрсетеді:

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver>
        <!-- біз тек қолданбаға мамандандырылған пакеттегі кластарды байланыстырамыз -->
        <include within="foo.*"/>
    </weaver>
    <aspects>
        <!-- біз тек осы аспектіні байланыстырамыз... -->
        <aspect name="foo.ProfilingAspect"/>
    </aspects>
</aspectj>

Енді Spring-ге тән конфигурация бөлігіне өтуге болады. LoadTimeWeaver-ді орнатуымыз керек (түсіндіру кейінірек болады). Бұл жүктеу уақытындағы байланыстыру құралы META-INF/aop.xml файлындағы бір немесе бірнеше кластарды қолданбаңыздың кластарымен байланыстыруға жауапты негізгі компонент болып табылады. Бақытымызға орай, ол күрделі конфигурацияны қажет етпейді (кейбір параметрлерді орнатуға болады, бірақ олар кейінірек сипатталады), келесі мысалдан көрініп тұрғандай:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- қызмет көрсету объектісі; біз оның әдістерін профилирлейміз -->
    <bean id="entitlementCalculationService"
            class="foo.StubEntitlementCalculationService"/>
    <!-- бұл жүктеу уақытындағы байланыстыруды қосады -->
    <context:load-time-weaver/>
</beans>

Енді қажетті артефактілер (аспект, META-INF/aop.xml файлы және Spring конфигурациясы) орнында болғандықтан, LTW механизмін әрекетте көрсету үшін main(..) әдісімен келесі драйвер класын жасай аламыз:

Java
package foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class);
        EntitlementCalculationService entitlementCalculationService =
                (EntitlementCalculationService) ctx.getBean("entitlementCalculationService");
        // профилировка аспекті осы әдістің орындалуын "қамтиды"
        entitlementCalculationService.calculateEntitlement();
    }
}
Kotlin
package foo
import org.springframework.context.support.ClassPathXmlApplicationContext
fun main() {
    val ctx = ClassPathXmlApplicationContext("beans.xml")
    val entitlementCalculationService = ctx.getBean("entitlementCalculationService") as EntitlementCalculationService
    // профилировка аспекті осы әдістің орындалуын "қамтиды"
    entitlementCalculationService.calculateEntitlement()
}

Бізге тек бір нәрсе жасау қажет. Бұл бөлімнің кіріспесінде Spring-дің көмегімен LTW механизмін әр класс жүктеуші негізінде қосуға болады делінген болатын және бұл шынында да солай. Алайда, бұл мысалда біз Spring-пен бірге берілетін Java агентін LTW механизмін қосу үшін қолданып отырмыз. Бұрын көрсетілген Main класын іске қосу үшін келесі команданы қолданамыз:

java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main

-javaagent флагы - бұл JVM-де орындалатын бағдарламаларды құрастыру агенттерін белгілеу және қосу үшін жалауша. Spring Framework құрамында -javaagent аргументінің мәні ретінде ұсынылған spring-instrument.jar ішіне жиналған InstrumentationSavingAgent агенті бар.

Main бағдарламасының орындалу нәтижесі келесідей болып көрінеді, бұл келесі мысалда көрсетілген. (Мен calculateEntitlement() іске асыруына Thread.sleep(..) нұсқаулығын енгіздім, бұл профилировщик нақты мәнді тіркегені үшін басқа 0 миллисекундтан басқа (01234 миллисекунд AOP енгізген кідіріс емес). Келесі тізім біздің профилировщиктің іске қосылуы кезінде алынған нәтижеден көрсетеді:

Calculating entitlement
StopWatch 'ProfilingAspect': running time (millis) = 1234
------ ----- ----------------------------
ms     %     Task name
------ ----- ----------------------------
01234  100%  calculateEntitlement

AspectJ-дің толық функционалды LTW арқылы, біз тек Spring бобтарының кеңестерін берумен шектелмейміз. Main бағдарламасы тақырыбына келесі кішкентай өзгеріс сол нәтижені береді:

Java
package foo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Main {
    public static void main(String[] args) {
        new ClassPathXmlApplicationContext("beans.xml", Main.class);
        EntitlementCalculationService entitlementCalculationService =
                new StubEntitlementCalculationService();
        // профилировка аспекті осы әдістің орындалуын "қамтиды"
        entitlementCalculationService.calculateEntitlement();
    }
}
Kotlin
package foo
import org.springframework.context.support.ClassPathXmlApplicationContext
fun main(args: Array<String>) {
    ClassPathXmlApplicationContext("beans.xml")
    val entitlementCalculationService = StubEntitlementCalculationService()
    // профилировка аспекті осы әдістің орындалуын "қамтиды"
    entitlementCalculationService.calculateEntitlement()
}

Алдыңғы бағдарламадан байқайсыз, біз Spring контейнерін жүктеп жатырмыз және содан кейін мүлдем Spring контекстісінен тыс StubEntitlementCalculationService жаңа экземплярын жасаймыз. Профилировка кеңестері бәрібір байланыстырылады.

Әрине, бұл мысал қарапайымдалған. Дегенмен, Spring-дегі LTW механизміне қолдаудың негізгі негіздері алдыңғы мысалда ұсынылған болатын, осы бөлімнің қалған бөлігінде әр конфигурация және қолданудың мәні егжей-тегжейлі сипатталады.

ProfilingAspect аспектісі, осы мысалда қолданылатын, қарапайым болуы мүмкін, бірақ өте пайдалы. Бұл әзірлеушілер әзірлеу кезінде қолдана алатын және содан кейін UAT (қабылдаушы пайдаланушы тестілеу ортасы) немесе өндірістік ортаға орналастыру кезінде қолданбалардың құрастыруларынан оңай шығарып алуға болатын әзірлеме уақытының аспектісінің жақсы мысалы.

Аспектілер

LTW-де қолданылатын аспектілер AspectJ аспектілері болуы керек. Сіз оларды AspectJ тілінде өзінде жаза аласыз немесе @AspectJ стилінде өз аспектілеріңізді жаза аласыз. Содан кейін сіздің аспектілеріңіз әрі AspectJ, әрі Spring AOP қабылдай алатын аспектілер болады. Сонымен қатар, скомпилирленген аспектілер класы кластар жолында болуы қажет.

'META-INF/aop.xml'

AspectJ LTW инфрақұрылымы Java кластар жолында орналасқан (немесе тікелей, немесе, әдетте, jar файлдарында) бір немесе бірнеше META-INF/aop.xml файлдары арқылы конфигурацияланады.

Осы файлдың құрылымы мен мазмұны AspectJ-дің LTW арналған анықтамалық құжаттамасының бөлігі болып табылатын құжаттамада егжей-тегжейлі сипатталған. aop.xml файлы 100% AspectJ-де жазылғандықтан, біз оны мұнда сипаттамаймыз.

Қажетті кітапханалар (JARS)

AspectJ-ден Spring Framework-пен LTW қолдауды қолдану үшін сізге кем дегенде келесі кітапханалар қажет болады:

  • spring-aop.jar

  • aspectjweaver.jar

Егер сіз Spring ұсынатын құралды қосу үшін агент қолдансаңыз, сізге сонымен қатар қажет:

  • spring-instrument.jar

Spring Конфигурациясы

Spring-дегі LTW механизміне қолдаудың негізгі компоненті - бұл LoadTimeWeaver интерфейсі (org.springframework.instrument.classloading пакетінде) және Spring дистрибутивімен бірге жеткізілетін оның көптеген іске асырылымдары. LoadTimeWeaver бір немесе бірнеше java.lang.instrument.ClassFileTransformers-ті орындалу уақытында ClassLoader -ге қосуға жауапты, бұл көптеген қызықты қолдану режимдерінің есіктерін ашу үшін, олардың бірі - жүктеу уақытында аспектілерді байланыстыру.

Егер сіз орындалу уақытында класстар файлдарын түрлендіру идеясымен таныс болмасаңыз, осы бөлімді жалғастырмас бұрын java.lang.instrument пакеті үшін API құжаттамасына қарау ұсынылады. Бұл құжаттама толық болмай тұрса да, сіз негізгі интерфейстер мен кластарды (осы бөлімді оқыған кезде анықтама үшін) көре алуыңыз мүмкін.

Белгілі бір ApplicationContext үшін LoadTimeWeaver-ді конфигурациялау бір жолды қосуға айналуы мүмкін. (Назар аударыңыз, сізге әдетте Spring контейнері ретінде ApplicationContext пайдалану қажет, әдетте BeanFactory жеткіліксіз, себебі LTW қолдауы BeanFactoryPostProcessors пайдаланады).

Spring Framework-де LTW механизміне қолдауды активтендіру үшін, LoadTimeWeaver-ді конфигурациялау қажет, бұл әдетте төменде көрсетілгендей @EnableLoadTimeWeaving аннотациясын қолдану арқылы жасалады:

Java
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
Kotlin
@Configuration
@EnableLoadTimeWeaving
class AppConfig {
}

Баламасы ретінде, егер сіз XML негізіндегі конфигурацияны ұнатсаңыз, <context:load-time-weaver/> элементін пайдалануға болады. Назар аударыңыз, элемент context аттық кеңістігінде анықталған. Келесі мысалда <context:load-time-weaver/> қалай пайдалану керектігі көрсетілген:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:load-time-weaver/>
</beans>

Алдыңғы конфигурация автоматты түрде сіз үшін LTW-ге тән, LoadTimeWeaver және AspectJWeavingEnabler сияқты бірқатар инфрақұрылымдық бобтарды анықтайды және тіркейді. Әдетте LoadTimeWeaver – бұл DefaultContextLoadTimeWeaver класы, ол автоматты түрде табылған LoadTimeWeaver-ді толықтыруға тырысады. Дәл LoadTimeWeaver түрі, ол "автоматты түрде анықталады", сіздің орындалу ортаңызға байланысты. Келесі кестеде LoadTimeWeaver-дің әртүрлі іске асырылымдары көрсетілген:

Кесте 13. DefaultContextLoadTimeWeaver LoadTimeWeaver
Орындалу ортасы Реализация LoadTimeWeaver

Apache Tomcat орындалуында

TomcatLoadTimeWeaver

GlassFish орындалуында (EAR орналастырумен шектеледі)

GlassFishLoadTimeWeaver

JBoss AS немесе WildFly орындалуында Red Hat-тан

JBossLoadTimeWeaver

WebSphere орындалуында IBM-нан

WebSphereLoadTimeWeaver

WebLogic орындалуында Oracle-дан

WebLogicLoadTimeWeaver

JVM іске қосылды InstrumentationSavingAgent(java -javaagent:path/to/spring-instrument.jar) Spring-ден

InstrumentationLoadTimeWeaver

Саяз келісімдер ClassLoader-тардың орындалуын күту, яғни addTransformer және, опциональды түрде, getThrowawayClassLoader әдісін.

ReflectiveLoadTimeWeaver

Кестеде тек LoadTimeWeaver-лер, олар DefaultContextLoadTimeWeaver қолданған кезде автоматты түрде анықталады, келтірілген. Сіз қандай LoadTimeWeaver жасырма белгілеу арқылы қолданылатынын нақты анықтай аласыз.

Java конфигурациясы арқылы нақты LoadTimeWeaver-ді орнату үшін LoadTimeWeavingConfigurer интерфейсін іске асырыңыз және getLoadTimeWeaver() әдісін қайта жазыңыз. Келесі мысалда ReflectiveLoadTimeWeaver анықталған:

Java
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {
    @Override
    public LoadTimeWeaver getLoadTimeWeaver() {
        return new ReflectiveLoadTimeWeaver();
    }
}
Kotlin
@Configuration
@EnableLoadTimeWeaving
class AppConfig : LoadTimeWeavingConfigurer {
    override fun getLoadTimeWeaver(): LoadTimeWeaver {
        return ReflectiveLoadTimeWeaver()
    }
}

Егер сіз XML негізіндегі конфигурацияны пайдалансаңыз, weaver-class атрибутының мәні ретінде толық класстар атауын <context:load-time-weaver/> элементіне ұсынылғанға қосуға болады. Келесі мысалда ReflectiveLoadTimeWeaver анықталған:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:load-time-weaver
            weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
</beans>

Конфигурациямен анықталған және тіркелген LoadTimeWeaver кейінірек Spring контейнерінен loadTimeWeaver жалпыға танымал атпен алынуы мүмкін. LoadTimeWeaver тек Spring-дағы LTW инфрақұрылымы үшін механизм ретінде бар екенін ұмытпаңыз, әрқайсысы біт ClassFileTransformers қосуға мүмкіндік береді. Нақты ClassFileTransformer, ол LTW орындайтын, бұл ClassPreProcessorAgentAdapter классы (пакет org.aspectj.weaver.loadtime ішінен). Қосымша ақпаратты ClassPreProcessorAgentAdapter класының javadoc құжаттамасынан таба аласыз, себебі байланыстыру қалай орындалатынының спецификасы осы құжаттың ішінде қамтылмайды.

Конфигурацияның қалған соңғы атрибутын қарастыруымыз керек: aspectjWeaving (немесе XML-де aspectj-weaving) атрибуты. Бұл атрибут LTW механизмінің қосылған немесе қосылмағанын басқарады. Ол үш мүмкін мәндердің бірін қабылдайды, және атрибут болмаған жағдайда, әдепкі мәні autodetect болып табылады. Келесі кестеде үш мүмкін мәндер келтірілген:

Кесте 14. AspectJ байланыстыру атрибут мәндері
Аннотация мәні XML мәні Түсініктемелер

ENABLED

қосылған

AspectJ байланыстыру қосылған, және аспектілердің байланыстыруы жүктеу уақытында қажетті жағдайларда орындалады.

DISABLED

өшірулі

Жүктеу уақытындағы байланыстыру өшірілген. Аспектілердің жүктеу уақытындағы байланыстыруы орындалмайды.

AUTODETECT

автоматты анықтау

Егер Spring-дегі LTW инфрақұрылымы кем дегенде бір META-INF/aop.xml файлды тапса, онда AspectJ байланыстыруы іске қосылады. Керісінше жағдайда, байланыстыру өшіріледі. Бұл әдепкі мән.

Арнайы орта үшін конфигурация

Бұл соңғы бөлім қолданба серверлері мен веб-контейнерлер сияқты орталарда Spring-мен LTW қолдауды қолданғанда қажетті болатын қосымша параметрлер мен конфигурацияны қамтиды.

Tomcat, JBoss, WebSphere, WebLogic

Tomcat, JBoss/WildFly, IBM WebSphere Application Server және Oracle WebLogic Server - олардың бәрі құрылымдық инструменттеуді жүзеге асыра алатын жалпы ClassLoader-ді қамтамасыз етеді. Spring-ден табиғи LTW, AspectJ-ден байланыстыруды қамтамасыз ету үшін осы ClassLoader іске асыруларын қолдана алады.Жүктеу уақытындағы байланыстыруды жай қосуға болады. Атап айтқанда, JVM іске қосу сценарийін -javaagent:path/to/spring-instrument.jar қосу үшін өзгерту қажет емес.

JBoss жағдайында, қолданба іске қосылғанға дейін кластарды жүктеуін болдырмау үшін сервістік сервердің сканерлеуін өшіру қажет болуы мүмкін. Жылдам шешім ретінде сіздің артефактке келесі мазмұны бар WEB-INF/jboss-scanning.xml деген атауы бар файлды қосу мүмкін:

<scanning xmlns="urn:jboss:scanning:1.0"/>

Универсальды Java қолдану

Егер нақты LoadTimeWeaver іске асыруларымен қолдау көрсетілмейтін орталардың кластарын құралдарынан өткізу қажет болса, жалпы шешім JVM агентін қолдану болып табылады. Осындай жағдайлар үшін Spring-де InstrumentationLoadTimeWeaver бар, ол Spring үшін арнайы (бірақ өте әмбебап) JVM агентін spring-instrument.jar талап етеді, оны @EnableLoadTimeWeaving және <context:load-time-weaver/> жалпы теңшеулер арқылы автоматты түрде табуға болады.

Оны қолдану үшін, сіз виртуалды машинаны Spring-ден агентпен келесі параметрлерді JVM-ге орнатып іске қосуыңыз қажет:

-javaagent:/path/to/spring-instrument.jar

Бұл JVM іске қосу сценарийін өзгерту қажет екенін атап өту қажет, бұл қосымша серверлер ортасында оны қолдануға кедергі келтіруі мүмкін (сервер мен қызмет көрсету саясаттарына байланысты). Дегенмен, бір қолданбаға бір JVM сияқты қолданба орындары сценарийлерінде, әдетте бүкіл JVM теңшеуді бақылауға тура келеді.

Қосымша ресурстар

AspectJ туралы қосымша ақпаратты AspectJ веб-сайтында табуға болады.

Адриан Колие және басқалары (Addison-Wesley, 2005) жазған Eclipse AspectJ AspectJ тілінің толық кіріспесі және анықтамасын ұсынады.

Рамнивас Ладдадтың (Manning, 2009) "AspectJ в действии" екінші басылымы қатты ұсынылады. Кітап негізінен AspectJ-ге арналған, бірақ АОП-ге қатысты көптеген жалпы тақырыптар да зерттеледі (достаточно глубоко).

Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION