Жүктеу уақытындағы байланыстыру (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-дің жылдам мысалын қарастырайық, содан кейін мысалдағы элементтерді тереңірек талдаймыз.
Бірінші мысал
Қолданба әзірлеушісі ретінде сізге кейбір өнімділік мәселелерін диагностикалау тапсырылды деп есептейік. Профилирлеу құралын қолданудың орнына, біз кейбір өнімділік көрсеткіштерін тез алу үшін қарапайым профилирлеу аспектісін қосамыз. Осыдан кейін бұл нақты аймаққа неғұрлым нақтырақ профилирлеу құралын қолдануға болады.
<context:load-time-weaver/>
баламасы ретінде
@EnableLoadTimeWeaving
аннотациясын қолдануға болады.
Келесі мысалда профилирлеудің тым ерекше емес аспектісі көрсетілген. Бұл @AspectJ стиліндегі аспектілерді жариялайтын уақытқа тәуелді профилирлеуші:
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(){}
}
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(..)
әдісімен келесі драйвер класын жасай аламыз:
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();
}
}
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
бағдарламасы тақырыбына келесі кішкентай өзгеріс сол нәтижені береді:
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();
}
}
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
аннотациясын қолдану арқылы жасалады:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
@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
-дің әртүрлі іске асырылымдары көрсетілген:
Орындалу ортасы | Реализация LoadTimeWeaver |
---|---|
Apache Tomcat орындалуында |
|
GlassFish орындалуында (EAR орналастырумен шектеледі) |
|
|
|
WebSphere орындалуында IBM-нан |
|
WebLogic орындалуында Oracle-дан |
|
JVM іске қосылды |
|
Саяз келісімдер ClassLoader-тардың орындалуын күту, яғни |
|
Кестеде тек LoadTimeWeaver
-лер, олар DefaultContextLoadTimeWeaver
қолданған кезде автоматты түрде анықталады, келтірілген. Сіз қандай LoadTimeWeaver
жасырма белгілеу арқылы қолданылатынын нақты анықтай аласыз.
Java конфигурациясы арқылы нақты LoadTimeWeaver
-ді орнату үшін LoadTimeWeavingConfigurer
интерфейсін іске асырыңыз және getLoadTimeWeaver()
әдісін қайта жазыңыз. Келесі мысалда ReflectiveLoadTimeWeaver
анықталған:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {
@Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new ReflectiveLoadTimeWeaver();
}
}
@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
болып табылады. Келесі кестеде үш мүмкін мәндер келтірілген:
Аннотация мәні | XML мәні | Түсініктемелер |
---|---|---|
|
|
AspectJ байланыстыру қосылған, және аспектілердің байланыстыруы жүктеу уақытында қажетті жағдайларда орындалады. |
|
|
Жүктеу уақытындағы байланыстыру өшірілген. Аспектілердің жүктеу уақытындағы байланыстыруы орындалмайды. |
|
|
Егер Spring-дегі LTW инфрақұрылымы кем дегенде бір |
Арнайы орта үшін конфигурация
Бұл соңғы бөлім қолданба серверлері мен веб-контейнерлер сияқты орталарда 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-ге арналған, бірақ АОП-ге қатысты көптеген жалпы тақырыптар да зерттеледі (достаточно глубоко).
GO TO FULL VERSION