Yuklash vaqtida bog'lash (Load-time weaving/LTW) deganda AspectJ aspektlarini Java virtual mashinasiga (Java virtual machine/JVM) yuklash vaqtida ilova klass fayllariga bog'lash jarayoni tushuniladi. Ushbu bo'limda Spring Framework kontekstida LTW mexanizmini sozlash va ishlatishga diqqat qaratilgan. Ushbu bo'lim umumiy LTW mexanizmi bo'yicha kirish bo'limi emas. Yuklash vaqtida bog'lashning o'ziga xos xususiyatlari va uni faqat AspectJ dan foydalanib sozlash haqida to'liq ma'lumot olish uchun AspectJ dasturlash muhitidagi yuklash vaqtida bog'lashga bag'ishlangan bo'limga qarang.
AspectJ LTW mexanizmi uchun Spring Frameworkning qiymati shundaki, u bog'lash jarayonini ancha nozik nazorat qilish imkonini beradi. AspectJ ning vanilya yuklash vaqtida bog'lash mexanizmi Java agenti (5+) orqali amalga oshiriladi, u JVMni ishga tushirishda virtual mashina argumentini belgilash orqali yoqiladi. Shu tariqa, bu butun JVM uchun parametr bo'lib, ba'zi holatlarda yaxshi bo'lishi mumkin, lekin ko'pincha juda qo'pol. Spring qo'llab-quvvatlagan LTW mexanizmi har bir ClassLoaderga asoslangan ravishda uni yoqish imkonini beradi, bu ancha nozik parametr bo'lib, bir JVMda ko'p dasturlar bilan (masalan, odatiy dastur serveri muhitida bo'lgani kabi) ancha maqsadga muvofiq bo'lishi mumkin.
Bundan tashqari, ma'lum muhitlarda ushbu qo'llab-quvvatlash ilova serveri ishga tushirish skriptida -javaagent:path/to/aspectjweaver.jar yoki (bu bo'limda keyinchalik tasvirlanadi) -javaagent:path/to/spring-instrument.jar ni qo'shish uchun o'zgartirishsiz yuklash vaqtida bog'lash imkonini beradi. Dasturchilar yuklash vaqtida bog'lashni yoqish uchun ilova kontekstini sozlaydilar, launch skriptlari uchun mas'ul bo'lgan administratorlarga bog'lanmagan holda.
Hozir, biz maqtalarning oxiriga yetgach, Spring yordamida AspectJ LTWning tez misolini ko'rib chiqamiz, so'ngra misolda taqdim etilgan elementlarni batafsil tahlil qilamiz.
Birinchi misol
Aytaylik, siz ilova dasturchisisiz va sizga tizimning ba'zi samaradorlik muammolarini aniqlash topshirildi. Profil uskunasidan foydalanish o'rniga, biz oddiy profiling aspektini qo'shamiz, bu tezda ba'zi samaradorlik ko'rsatkichlarini olish imkonini beradi. Darhol so'ngra, ushbu maxsus sohada yanada nozik profiling vositasini qo'llash mumkin.
@EnableLoadTimeWeaving anotatsiyasini
<context:load-time-weaver/> ga alternativa sifatida ishlatishingiz mumkin.
Quyidagi misol unchalik murakkab bo'lmagan profiling aspektini ko'rsatadi. Bu vaqt asosida nazorat qilinadigan profiling vositasi @AspectJ uslubidagi aspektlar e'lon qilishdan foydalanadi:
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() {
}
}
Bizga META-INF/aop.xml faylini ham yaratish kerak bo'ladi, bu orqali AspectJ bog'lash vositasiga biz ProfilingAspect ni o'z klasslarimizga bog'lashni xohlayotganimizni aytamiz. Fayllar uchun ushbu kelishuv, ya'ni Java klass yo'lida META-INF/aop.xml deb nomlangan fayl(lari) mavjudligi AspectJ standarti hisoblanadi. Quyidagi misol aop.xml faylini ko'rsatadi:
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- faqat ilovaga xos paketlardagi klasslarni bog'laymiz -->
<include within="foo.*"/>
</weaver>
<aspects>
<!-- faqat ushbu aspekt bog'lanadi... -->
<aspect name="foo.ProfilingAspect"/>
</aspects>
</aspectj>
Endi Spring uchun o'ziga xos konfiguratsiya qismiga o'tish mumkin. Biz LoadTimeWeaver ni sozlashimiz kerak bo'ladi (keyinroq tushuntiriladi). Ushbu bog'lash vositasi yuklash vaqtida bog'lash uchun asosiy komponent hisoblanadi va u qaysi konfiguratsiya aspektlarini bir yoki bir nechta META-INF/aop.xml fayllarida ilova klasslariga bog'lash uchun javobgardir. Yaxshiyamki, uni sozlash murakkab emas (bir nechta variantlarni belgilash mumkin, lekin ular keyinchalik batafsil tasvirlanadi), quyidagi misoldan ko'rinib turganidek:
<?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">
<!-- хизмат объекти; uning metodlarini profil qilamiz -->
<bean id="entitlementCalculationService"
class="foo.StubEntitlementCalculationService"/>
<!-- bu yuklash vaqtida bog'lashni yoqadi -->
<context:load-time-weaver/>
</beans>
Endi, kerakli barcha artefaktlar (aspekt, META-INF/aop.xml fayli va Spring konfiguratsiyasi) mavjud bo'lgach, biz LTW mexanizmini amalda ko'rsatish uchun main(..) metodi bilan haydovchi klassni yaratishimiz mumkin:
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");
// profiling aspekti ushbu metodning bajarilishini "o'rab oladi"
entitlementCalculationService.calculateEntitlement();
}
}
package foo
import org.springframework.context.support.ClassPathXmlApplicationContext
fun main() {
val ctx = ClassPathXmlApplicationContext("beans.xml")
val entitlementCalculationService = ctx.getBean("entitlementCalculationService") as EntitlementCalculationService
// profiling aspekti ushbu metodning bajarilishini "o'rab oladi"
entitlementCalculationService.calculateEntitlement()
}
Bizga faqat bitta ish qoldi. Ushbu bo'limning kirish qismida aytilganidek, Spring yordamida LTW mexanizmini har bir ClassLoader asosida tanlab yoqish mumkin va bu haqiqatda shunday. Biroq, ushbu misolda biz LTW mexanizmini yoqish uchun (Spring bilan taqdim etilgan) Java agentidan foydalanamiz. Biz avvalgi misolda ko'rsatilgan Main klassini ishga tushirish uchun quyidagi buyruqdan foydalanamiz:
java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main
-javaagent flagi, JVMda ishlaydigan dasturlarni asboblarga ajratish agentlarini belgilash va yoqish uchun flag hisoblanadi. Spring Framework tarkibida bunday agent, InstrumentationSavingAgent, spring-instrument.jar ichida qadoqlangan, u avvalgi misolda -javaagent argumenti qiymati sifatida kiritildi.
Main dasturining ishlash natijasi taxminan quyidagicha ko'rinishga ega. (Men calculateEntitlement() ning amalga oshirilishiga Thread.sleep(..) buyruqni kiritdim, shunda profiling vositasi 0 millisekunddan boshqa biror qiymatni haqiqatda qayd etsinki (01234 millisekund tafovutni AOP kiritgan emas). Quyidagi listingda profiling vositamizni ishga tushirishda olingan natijani ko'rsatadi:
Calculating entitlement StopWatch 'ProfilingAspect': running time (millis) = 1234 ------ ----- ---------------------------- ms % Task name ------ ----- ---------------------------- 01234 100% calculateEntitlement
Chunki LTW mexanizmi to'liq funksional AspectJ orqali amalga oshiriladi, biz faqat Spring bobalariga maslahatlar berish bilan cheklanmaymiz. Quyidagi Main dasturidan kichik bir variant xuddi o'sha natijani beradi:
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();
// profiling aspekti ushbu metodning bajarilishini "o'rab oladi"
entitlementCalculationService.calculateEntitlement();
}
}
package foo
import org.springframework.context.support.ClassPathXmlApplicationContext
fun main(args: Array<String>) {
ClassPathXmlApplicationContext("beans.xml")
val entitlementCalculationService = StubEntitlementCalculationService()
// profiling aspekti ushbu metodning bajarilishini "o'rab oladi"
entitlementCalculationService.calculateEntitlement()
}
E'tibor bering, oldingi dasturda biz Spring konteynerini yuklaymiz, so'ngra Spring kontekstidan butunlay tashqarida yangi StubEntitlementCalculationService instansiyasini yaratamiz. Profiling maslahatlari baribir bog'langan.
Albatta, misol soddalashtirilgan. Biroq, Springga LTW mexanizmi qo'llab-quvvatlashi asoslari oldingi misolda keltirilgan edi va ushbu bo'limning qolgan qismida har bir konfiguratsiya va qo'llash mazmuni batafsil bayon etilgan.
ProfilingAspect asosiy bo'lishi mumkin, ammo u juda foydali. Bu dasturlash vaqtida foydalanish uchun yaxshi misol bo'lib, dasturchilar uni rivojlanish vaqtida ishlatishi mumkin va keyin uni qabul qilishni foydalanuvchi testi (UAT) yoki amaliy muhitda dasturlarni yig'ishdan osongina chiqarib tashlashi mumkin.
Aspektlar
LTWda foydalanadigan aspektlar AspectJ aspektlari bo'lishi kerak. Siz ularni AspectJ tilida yoki @AspectJ uslubida yozishingiz mumkin. Shunda sizning aspektlaringiz ham AspectJ, ham Spring AOP tomonidan qabul qilinadigan bo'ladi. Bundan tashqari, kompilyatsiya qilingan aspekt klasslari klass yo'lida mavjud bo'lishi kerak.
'META-INF/aop.xml'
AspectJ LTW infrastrukturasini Java klass yo'lida (yoki bevosita, yoki, ko'proq, jar-fayllarda) joylashgan bir yoki bir nechta META-INF/aop.xml fayllarini ishlatib konfiguratsiya qilingan.
Ushbu faylning struktura va mazmuni LTWga bag'ishlangan AspectJ hujjatiga tegishlidir, va aop.xml fayli 100% AspectJda yozilganligi sababli, biz uni bu erda tasvirlamaymiz.
Talab qilinadigan kutubxonalar (JARS)
AspectJ LTW mexanizmini Spring Frameworkda ishlatish uchun, kamida quyidagi kutubxonalar kerak bo'ladi:
-
spring-aop.jar -
aspectjweaver.jar
Agar siz Spring tomonidan taqdim etilgan asbobga agentni faollashtirish uchun foydalanayotgan boʻlsangiz, sizga ham quyidagi kutubxonalar kerak boʻladi:
-
spring-instrument.jar
Springni konfiguratsiya qilish
LTW mexanizmini Springda qo'llab-quvvatlashning kalit komponenti - bu LoadTimeWeaver ( org.springframework.instrument.classloading paketida) interfeysi va ko'plab Spring tarqatmasi bilan ta'minlangan realizatsiyalari. LoadTimeWeaver bajarilish vaqtida bir yoki bir nechta java.lang.instrument.ClassFileTransformersni ClassLoaderga qo'shish uchun javobgardir, bu esa turli xil qiziqarli qo'llanilish usullari uchun eshiklarni ochadi, ulardan biri yuklash vaqtida aspektlarni bog'lashdir.
java.lang.instrument paketi uchun API hujjatlariga murojaat qiling. Garchi bu hujjat to'liq bo'lmasa-da, hech bo'lmaganda asosiy interfeyslar va klasslarni ko'ra olasiz (bu bo'limni o'qish uchun ma'lumot uchun).
Agar ApplicationContext uchun LoadTimeWeaverni sozlash amalda bir satr qo'shishga kelib qolishi mumkin (E'tibor bering, siz ApplicationContextni Spring konteyneri sifatida ishlatishingiz juda muhim - odatda, BeanFactory etarli emas, chunki LTW mexanizmini qo'llab-quvvatlash BeanFactoryPostProcessors ishlatadi).
Spring Frameworkda LTW mexanizmini qo'llab-quvvatlashni yoqish uchun LoadTimeWeaverni sozlashingiz kerak, bu odatda quyidagi kabi @EnableLoadTimeWeaving anotatsiyasidan foydalanish orqali amalga oshiriladi:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
@Configuration
@EnableLoadTimeWeaving
class AppConfig {
}
Yoki, agar siz XML asosidagi konfiguratsiyani afzal ko'rsangiz, <context:load-time-weaver/> elementidan foydalaning. E'tibor bering, element context nomli makonda aniqlangan. Quyidagi misolda <context:load-time-weaver/> dan qanday foydalanish ko'rsatilgan:
<?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>
Yuqoridagi konfiguratsiya siz uchun LTW uchun maxsus infrastruktur bobalarini, masalan, LoadTimeWeaver va AspectJWeavingEnabler avtomatik aniqlash va ro'yxatdan o'tkazadi. Standart, LoadTimeWeaver - bu DefaultContextLoadTimeWeaver klassi bo'lib, aniqlangan LoadTimeWeaverni qo'shishga harakat qiladi. Aniq LoadTimeWeaver turi, "aniqlangan avtomatik" bo'lib ishlaydi, sizning ish muhitingizga bog'liq. Quyidagi jadvalda LoadTimeWeaver ning turli xil realizatsiyalari keltirilgan:
| Ish muhiti | Reallzatsiya LoadTimeWeaver |
|---|---|
| Apache Tomcatda amalga oshirilishi |
|
| GlassFishda amalga oshirilishi (EAR deployment bilan cheklangan) |
|
| Red Hat JBoss AS yoki WildFlyda amalga oshirilishi |
|
| IBM WebSphereda amalga oshirilishi |
|
| Oracle WebLogicda amalga oshirilishi |
|
| JVM |
|
| Umumiy kelishuvlar (aniqroqqa |
|
E'tibor bering, jadvalda faqat LoadTimeWeaver ko'rsatilgan, bu DefaultContextLoadTimeWeaverdan foydalanganda avtomatik aniqlanadi. LoadTimeWeaverning qaysi realizatsiyasidan foydalanishni aniq belgilashingiz mumkin.
Java-konfiguratsiyasidan foydalanib, aniq LoadTimeWeaverni belgilash uchun LoadTimeWeavingConfigurer interfeysini amalga oshiring va getLoadTimeWeaver() metodini qayta belgilab bering. Quyidagi misolda ReflectiveLoadTimeWeaver belgilangan:
@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()
}
}
Agar XML asosida konfiguratsiyadan foydalanayotgan bo'lsangiz, <context:load-time-weaver/> elementidagi weaver-class atributiga klassning to'liq nomini qiymati sifatida kiritishingiz mumkin. Yana, quyidagi misolda ReflectiveLoadTimeWeaver aniq belgilangan:
<?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>
The LoadTimeWeaver that is determined and registered by the configuration can later be obtained from the Spring container using the well-known name loadTimeWeaver. Remember that LoadTimeWeaver exists only as a mechanism for the LTW infrastructure in Spring, allowing one or more ClassFileTransformers to be added. The actual ClassFileTransformer that performs the LTW is the ClassPreProcessorAgentAdapter class (from the org.aspectj.weaver.loadtime package). More detailed information can be found in the class ClassPreProcessorAgentAdapter javadoc, as the specifics of how the weaving occurs are beyond the scope of this document.
The last configuration attribute to consider: the aspectjWeaving attribute (or aspectj-weaving if you are using XML). This attribute controls whether the LTW mechanism is enabled or not. It takes one of three possible values, with the default value being autodetect if the attribute is absent. The following table lists the three possible values:
| Anotatsiya qiymati | XML qiymati | Izoh |
|---|---|---|
|
|
|
AspectJ bog'lash yoqilgan, va aspektlarni yuklash vaqtida bog'lash kerak bo'lganda amalga oshadi. |
|
|
|
Yuklash vaqtida bog'lash o'chirilgan. Yuklash vaqtida aspektlarni bog'lash sodir bo'lmaydi. |
|
|
|
Agar Springdagi LTW infratuzilmasi hech bo'lmaganda bitta |
Muayyan muhit uchun konfiguratsiya
Ushbu oxirgi bo'lim Springdan AspectJ LTW mexanizmini muayyan muhitlarda, masalan, dastur serverlari va veb-konteynerlarda ishlatishda kerakli barcha qo'shimcha parametrlar va konfiguratsiyalarni qamrab oladi.
Tomcat, JBoss, WebSphere, WebLogic
Tomcat, JBoss/WildFly, IBM WebSphere Application Server va Oracle WebLogic Server - ularning barchasi dasturlar uchun umumiy ClassLoaderni taqdim etadi, u mahalliy asboblashni amalga oshira oladi. Springning mahalliy LTWsi AspectJ-dan bog'lashni ta'minlash uchun ushbu ClassLoader realizatsiyalaridan foydalanishi mumkin. Yuklash vaqtida bog'lashni yoqishingiz mumkin. Ayniqsa, -javaagent:path/to/spring-instrument.jar qo'shish uchun JVM ishga tushirish skriptini o'zgartirishning hojati yo'q.
Diqqat eting: JBoss uchun ilovalar serveri skanerlashini o'chirib qo'yish kerak bo'lishi mumkin, shunda u dastur haqiqatan ishga tushgunga qadar klasslarni yuklamaydi. Tez yechim - bu WEB-INF/jboss-scanning.xml nomli faylni arifaktni qo'shib qo'yish, quyidagi tarkib bilan:
<scanning xmlns="urn:jboss:scanning:1.0"/>
Universal Java Foydalanish
Agar ma'lum realizatsiyalar LoadTimeWeaver tomonidan qo'llab-quvvatlanmaydigan muhitlarda klasslarni asboblash kerak bo'lsa, umumiy yechim JVM agenti hisoblanadi. Bunday holatlar uchun, Springda universial agentni talab qiluvchi InstrumentationLoadTimeWeaver mavjud, spring-instrument.jar, va u @EnableLoadTimeWeaving va <context:load-time-weaver/> umumiy sozlashlari tomonidan avtomatik aniqlanadi.
Uni ishlatish uchun, quyidagi buyruqlarni JVM uchun belgilangan agent bilan virtual mashinani ishga tushirishingiz kerak:
-javaagent:/path/to/spring-instrument.jar
Diqqat qiling, bu JVM ishga tushirish skriptini o'zgartirishni talab qiladi va bu, server va ekspluatatsiya siyosatlaringizga qarab, uni ilova serverlari muhitida ishlatishga to'sqinlik qilishi mumkin. Shunga qaramasdan, Spring Boot singari ilova deployment ssenariylari, bir JVMda bitta ilova usulida, siz odatda JVMni to'liq sozlashni nazorat qilishni nazorat qilasiz.
Qo'shimcha manbalar
AspectJ haqida batafsil ma'lumotni AspectJ veb-saytida topishingiz mumkin.
Adrian Kolle va boshqalar tomonidan yozilgan Eclipse AspectJ (Addison-Wesley, 2005) AspectJ tiliga to'liq kirish va qo'llanmaning kirishini taqdim etadi.
Yana tavsiya etilishi kerak bo'lgan "AspectJ amalda" ikkinchi nashri - Ramnivas Laddad tomonidan yozilgan (Manning, 2009) kitobi. Kitobda AspectJning katta e'tibori mavjud, ammo ko'plab umumiy AOP mavzulari ham kiritilgan (ehtimol chuqurroq o'rganilgan).
GO TO FULL VERSION