
Paglalapat ng AOP
Ang programming na nakatuon sa aspeto ay idinisenyo upang magsagawa ng mga cross-cutting na gawain, na maaaring maging anumang code na maaaring ulitin nang maraming beses sa pamamagitan ng iba't ibang mga pamamaraan, na hindi maaaring ganap na ibalangkas sa isang hiwalay na module. Alinsunod dito, hinahayaan kami ng AOP na panatilihin ito sa labas ng pangunahing code at ideklara ito nang patayo. Ang isang halimbawa ay ang paggamit ng patakaran sa seguridad sa isang application. Karaniwan, ang seguridad ay tumatakbo sa maraming elemento ng isang application. Higit pa rito, ang patakaran sa seguridad ng application ay dapat na mailapat nang pantay-pantay sa lahat ng umiiral at bagong bahagi ng application. Kasabay nito, ang isang patakaran sa seguridad na ginagamit ay maaaring mag-evolve mismo. Ito ang perpektong lugar para gamitin ang AOP . Gayundin, ang isa pang halimbawa ay ang pag-log. Mayroong ilang mga pakinabang sa paggamit ng AOP na diskarte sa pag-log kaysa sa manu-manong pagdaragdag ng pag-log functional:-
Ang code para sa pag-log ay madaling idagdag at alisin: ang kailangan mo lang gawin ay magdagdag o mag-alis ng ilang mga configuration ng ilang aspeto.
-
Ang lahat ng source code para sa pag-log ay pinananatili sa isang lugar, kaya hindi mo kailangang manu-manong hanapin ang lahat ng mga lugar kung saan ito ginagamit.
-
Maaaring idagdag ang logging code kahit saan, maging sa mga pamamaraan at klase na naisulat na o sa bagong functionality. Binabawasan nito ang bilang ng mga error sa coding.
Gayundin, kapag nag-aalis ng isang aspeto mula sa isang configuration ng disenyo, makatitiyak kang wala na ang lahat ng tracing code at walang napalampas.
- Ang mga aspeto ay hiwalay na code na maaaring mapabuti at magamit nang paulit-ulit.

Mga pangunahing prinsipyo ng AOP
Para makasulong pa sa paksang ito, alamin muna natin ang mga pangunahing konsepto ng AOP. Payo — Karagdagang logic o code na tinatawag mula sa isang join point. Maaaring isagawa ang payo bago, pagkatapos, o sa halip na isang punto ng pagsali (higit pa tungkol sa mga ito sa ibaba). Mga posibleng uri ng payo :-
Bago — ang ganitong uri ng payo ay inilunsad bago isagawa ang mga target na pamamaraan, ie join points. Kapag gumagamit ng mga aspeto bilang mga klase, ginagamit namin ang @Before annotation upang markahan ang payo bilang nauna. Kapag gumagamit ng mga aspeto bilang .aj file, ito ang magiging before() na paraan.
- Pagkatapos — ang payo na isinasagawa pagkatapos ng pagpapatupad ng mga pamamaraan (pagsamahin ang mga puntos) ay kumpleto, kapwa sa normal na pagpapatupad pati na rin kapag naghagis ng eksepsiyon.
Kapag gumagamit ng mga aspeto bilang mga klase, maaari naming gamitin ang @After annotation upang ipahiwatig na ito ay payo na darating pagkatapos.
Kapag gumagamit ng mga aspeto bilang .aj file, ito ang after() na pamamaraan.
-
Pagkatapos ng Pagbabalik — ang payo na ito ay isinasagawa lamang kapag ang target na pamamaraan ay natapos nang normal, nang walang mga error.
Kapag ang mga aspeto ay kinakatawan bilang mga klase, maaari naming gamitin ang @AfterReturning annotation upang markahan ang payo bilang executing pagkatapos ng matagumpay na pagkumpleto.
Kapag gumagamit ng mga aspeto bilang .aj file, ito ang magiging after() returning (Object obj) na paraan.
-
After Throwing — ang payo na ito ay inilaan para sa mga pagkakataon kapag ang isang paraan, iyon ay, join point, ay naghagis ng exception. Magagamit namin ang payong ito upang pangasiwaan ang ilang uri ng nabigong pagpapatupad (halimbawa, upang ibalik ang isang buong transaksyon o mag-log na may kinakailangang antas ng bakas).
Para sa mga aspeto ng klase, ang @AfterThrowing annotation ay ginagamit upang isaad na ang payo na ito ay ginagamit pagkatapos maghagis ng exception.
Kapag gumagamit ng mga aspeto bilang .aj file, ito ang magiging after() throwing (Exception e) na paraan.
-
Sa paligid — marahil isa sa pinakamahalagang uri ng payo. Pinapalibutan nito ang isang paraan, iyon ay, isang punto ng pagsanib na maaari nating gamitin upang, halimbawa, piliin kung isasagawa o hindi ang isang ibinigay na paraan ng punto ng pagsasama.
Maaari kang magsulat ng code ng payo na tumatakbo bago at pagkatapos isagawa ang paraan ng join point.
Ang payo sa paligid ay may pananagutan sa pagtawag sa paraan ng pagsali sa punto at sa mga halaga ng pagbabalik kung ang pamamaraan ay nagbabalik ng isang bagay. Sa madaling salita, sa payong ito, maaari mo lamang gayahin ang pagpapatakbo ng isang target na paraan nang hindi ito tinatawag, at ibalik ang anumang gusto mo bilang resulta ng pagbabalik.
Dahil sa mga aspeto bilang mga klase, ginagamit namin ang @Around annotation para gumawa ng payo na bumabalot sa isang join point. Kapag gumagamit ng mga aspeto sa anyo ng mga .aj na file, ang pamamaraang ito ay ang around() na pamamaraan.
-
Compile-time weaving — kung mayroon kang source code ng aspeto at ang code kung saan mo ginagamit ang aspeto, maaari mong i-compile ang source code at ang aspeto nang direkta gamit ang AspectJ compiler;
-
Post-compile weaving (binary weaving) — kung hindi mo o hindi nais na gumamit ng mga pagbabago sa source code upang ihabi ang mga aspeto sa code, maaari kang kumuha ng mga dating pinagsama-samang klase o jar file at mag-inject ng mga aspeto sa mga ito;
-
Load-time weaving — isa lang itong binary weaving na naaantala hanggang sa i-load ng classloader ang class file at tukuyin ang klase para sa JVM.
Ang isa o higit pang mga weaving class loader ay kinakailangan upang suportahan ito. Ang mga ito ay alinman sa tahasang ibinigay ng runtime o na-activate ng isang "weaving agent."
Mga halimbawa sa Java
Susunod, para sa mas mahusay na pag-unawa sa AOP , titingnan natin ang maliliit na halimbawa ng istilong "Hello World". Sa kanan ng paniki, mapapansin ko na ang aming mga halimbawa ay gagamit ng compile-time weaving . Una, kailangan naming idagdag ang sumusunod na dependency sa aming pom.xml file:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
Bilang isang tuntunin, ang espesyal na ajc compiler ay kung paano namin ginagamit ang mga aspeto. Hindi ito isinasama ng IntelliJ IDEA bilang default, kaya kapag pinili mo ito bilang compiler ng application, dapat mong tukuyin ang path patungo sa 5168 75 AspectJ distribution. Ito ang unang paraan. Ang pangalawa, na siyang ginamit ko, ay irehistro ang sumusunod na plugin sa pom.xml file:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
<<verbose>true<verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Pagkatapos nito, magandang ideya na muling mag-import mula sa Maven at patakbuhin ang mvn clean compile . Ngayon, magpatuloy tayo nang direkta sa mga halimbawa.
Halimbawa Blg. 1
Gumawa tayo ng Main class. Sa loob nito, magkakaroon tayo ng entry point at isang paraan na nagpi-print ng ipinasa na pangalan sa console:
public class Main {
public static void main(String[] args) {
printName("Tanner");
printName("Victor");
printName("Sasha");
}
public static void printName(String name) {
System.out.println(name);
}
}
Walang kumplikado dito. Nagpasa kami ng isang pangalan at ipinakita ito sa console. Kung patakbuhin natin ang programa ngayon, makikita natin ang sumusunod sa console:
public aspect GreetingAspect {
pointcut greeting() : execution(* Main.printName(..));
before() : greeting() {
System.out.print("Hi, ");
}
}
Ang file na ito ay parang isang klase. Tingnan natin kung ano ang nangyayari dito: ang pointcut ay isang set ng mga join point; greeting() ang pangalan ng pointcut na ito; : execution ay nagpapahiwatig na ilapat ito sa panahon ng pagpapatupad ng lahat ng ( * ) na tawag ng Main.printName(...) na pamamaraan. Susunod ang isang tiyak na payo — before() — na isinasagawa bago tawagin ang target na paraan. : greeting() ang cutpoint na tinutugon ng payong ito. Buweno, at sa ibaba ay makikita natin ang katawan ng pamamaraan mismo, na nakasulat sa wikang Java, na naiintindihan natin. Kapag nagpatakbo kami ng pangunahing kasama ang aspetong ito, makukuha namin ang output ng console na ito:
@Aspect
public class GreetingAspect{
@Pointcut("execution(* Main.printName(String))")
public void greeting() {
}
@Before("greeting()")
public void beforeAdvice() {
System.out.print("Hi, ");
}
}
Pagkatapos ng .aj aspect file, nagiging mas malinaw ang lahat dito:
- Ipinapahiwatig ng @Aspect na ang klase na ito ay isang aspeto;
- Ang @Pointcut("execution(* Main.printName(String))") ay ang cutpoint na na-trigger para sa lahat ng tawag sa Main.printName na may input argument na ang uri ay String ;
- Ang @Before("greeting()") ay payo na inilapat bago tawagan ang code na tinukoy sa greeting() cutpoint.
Halimbawa Blg. 2
Ipagpalagay na mayroon kaming ilang pamamaraan na nagsasagawa ng ilang mga operasyon para sa mga kliyente, at tinatawag namin ang pamamaraang ito mula sa pangunahing :
public class Main {
public static void main(String[] args) {
performSomeOperation("Tanner");
}
public static void performSomeOperation(String clientName) {
System.out.println("Performing some operations for Client " + clientName);
}
}
Gamitin natin ang @Around annotation para gumawa ng "pseudo-transaction":
@Aspect
public class TransactionAspect{
@Pointcut("execution(* Main.performSomeOperation(String))")
public void executeOperation() {
}
@Around(value = "executeOperation()")
public void beforeAdvice(ProceedingJoinPoint joinPoint) {
System.out.println("Opening a transaction...");
try {
joinPoint.proceed();
System.out.println("Closing a transaction...");
}
catch (Throwable throwable) {
System.out.println("The operation failed. Rolling back the transaction...");
}
}
}
Gamit ang proceed method ng ProceedingJoinPoint object, tinatawag namin ang wrapping method para matukoy ang lokasyon nito sa payo. Samakatuwid, ang code sa pamamaraan sa itaas ay joinPoint.proceed(); ay Bago , habang ang code sa ibaba nito ay Pagkatapos . Kung tatakbo tayo main , makukuha natin ito sa console:
public static void performSomeOperation(String clientName) throws Exception {
System.out.println("Performing some operations for Client " + clientName);
throw new Exception();
}
Pagkatapos ay makuha namin ang output ng console na ito:
Halimbawa Blg. 3
Sa aming susunod na halimbawa, gawin natin ang isang bagay tulad ng pag-log sa console. Una, tingnan ang Main , kung saan nagdagdag kami ng ilang pseudo business logic:
public class Main {
private String value;
public static void main(String[] args) throws Exception {
Main main = new Main();
main.setValue("<some value>");
String valueForCheck = main.getValue();
main.checkValue(valueForCheck);
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public void checkValue(String value) throws Exception {
if (value.length() > 10) {
throw new Exception();
}
}
}
Sa pangunahing , ginagamit namin ang setValue para magtalaga ng value sa value instance variable. Pagkatapos ay ginagamit namin ang getValue upang makuha ang halaga, at pagkatapos ay tinatawagan namin ang checkValue upang makita kung mas mahaba ito sa 10 character. Kung gayon, kung gayon ang isang pagbubukod ay itatapon. Ngayon tingnan natin ang aspeto na gagamitin natin upang mai-log ang gawain ng mga pamamaraan:
@Aspect
public class LogAspect {
@Pointcut("execution(* *(..))")
public void methodExecuting() {
}
@AfterReturning(value = "methodExecuting()", returning = "returningValue")
public void recordSuccessfulExecution(JoinPoint joinPoint, Object returningValue) {
if (returningValue != null) {
System.out.printf("Successful execution: method — %s method, class — %s class, return value — %s\n",
joinPoint.getSignature().getName(),
joinPoint.getSourceLocation().getWithinType().getName(),
returningValue);
}
else {
System.out.printf("Successful execution: method — %s, class — %s\n",
joinPoint.getSignature().getName(),
joinPoint.getSourceLocation().getWithinType().getName());
}
}
@AfterThrowing(value = "methodExecuting()", throwing = "exception")
public void recordFailedExecution(JoinPoint joinPoint, Exception exception) {
System.out.printf("Exception thrown: method — %s, class — %s, exception — %s\n",
joinPoint.getSignature().getName(),
joinPoint.getSourceLocation().getWithinType().getName(),
exception);
}
}
Anong nangyayari dito? Ang @Pointcut("execution(* *(..))") ay sasali sa lahat ng tawag sa lahat ng pamamaraan. Ang @AfterReturning(value = "methodExecuting()", returning = "returningValue") ay payo na isasagawa pagkatapos ng matagumpay na pagpapatupad ng target na paraan. Mayroon kaming dalawang kaso dito:
- Kapag may return value ang method — if (returningValue! = Null) {
- Kapag walang return value — else {

GO TO FULL VERSION