CodeGym/Java Blog/Random/Ano ang AOP? Mga prinsipyo ng programming na nakatuon sa ...
John Squirrels
Antas
San Francisco

Ano ang AOP? Mga prinsipyo ng programming na nakatuon sa aspeto

Nai-publish sa grupo
Hi, guys and gals! Nang walang pag-unawa sa mga pangunahing konsepto, medyo mahirap na bungkalin ang mga balangkas at diskarte sa pagbuo ng functionality. Kaya ngayon ay pag-uusapan natin ang tungkol sa isang ganoong konsepto — AOP, aka aspect-oriented programming . Ano ang AOP?  Mga prinsipyo ng programming na nakatuon sa aspeto - 1Ang paksang ito ay hindi madali at bihirang gamitin nang direkta, ngunit maraming mga framework at teknolohiya ang gumagamit nito sa ilalim ng hood. At siyempre, minsan sa panahon ng mga panayam, maaaring hilingin sa iyo na ilarawan sa pangkalahatang mga termino kung anong uri ng hayop ito at kung saan ito maaaring ilapat. Kaya tingnan natin ang mga pangunahing konsepto at ilang simpleng halimbawa ng AOP sa Java . Ngayon, ang AOP ay kumakatawan sa aspect-oriented programming, na isang paradigm na nilayon upang mapataas ang modularity ng iba't ibang bahagi ng isang application sa pamamagitan ng paghihiwalay ng mga cross-cutting na alalahanin. Upang magawa ito, ang karagdagang pag-uugali ay idinagdag sa umiiral na code nang hindi gumagawa ng mga pagbabago sa orihinal na code. Sa madaling salita, maaari nating isipin ito bilang pagbitin ng karagdagang pag-andar sa itaas ng mga pamamaraan at klase nang hindi binabago ang binagong code. Bakit kailangan ito? Maaga o huli, napagpasyahan namin na ang karaniwang object-oriented na diskarte ay hindi palaging epektibong malulutas ang ilang mga problema. At kapag dumating ang sandaling iyon, sumagip ang AOP at nagbibigay sa amin ng mga karagdagang tool para sa pagbuo ng mga application. At ang mga karagdagang tool ay nangangahulugan ng pagtaas ng kakayahang umangkop sa pagbuo ng software, na nangangahulugan ng higit pang mga opsyon para sa paglutas ng isang partikular na problema.

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:
  1. 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.

  2. 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.

  3. 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.

  4. Ang mga aspeto ay hiwalay na code na maaaring mapabuti at magamit nang paulit-ulit.
Ano ang AOP?  Mga prinsipyo ng programming na nakatuon sa aspeto - 2Ginagamit din ang AOP para sa paghawak ng exception, pag-cache, at pag-extract ng ilang partikular na functionality upang gawin itong magagamit muli.

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 :
  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

Join Point — ang punto sa isang tumatakbong programa (ibig sabihin, method call, object creation, variable access) kung saan dapat ilapat ang payo. Sa madaling salita, ito ay isang uri ng regular na expression na ginagamit upang maghanap ng mga lugar para sa iniksyon ng code (mga lugar kung saan dapat ilapat ang payo). Pointcut — isang set ng mga joint point . Tinutukoy ng isang pointcut kung ang ibinigay na payo ay naaangkop sa isang ibinigay na punto ng pagsali. Aspect — isang module o klase na nagpapatupad ng cross-cutting functionality. Binabago ng aspeto ang pag-uugali ng natitirang code sa pamamagitan ng paglalapat ng payo sa mga join point na tinukoy ng ilang pointcut . Sa madaling salita, ito ay isang kumbinasyon ng payo at sumali sa mga puntos. Panimula— pagpapalit ng istruktura ng isang klase at/o pagpapalit ng inheritance hierarchy upang idagdag ang functionality ng aspeto sa foreign code. Target — ang bagay kung saan ilalapat ang payo. Paghahabi — ang proseso ng pag-uugnay ng mga aspeto sa iba pang mga bagay upang lumikha ng mga pinapayong proxy na bagay. Magagawa ito sa oras ng pag-compile, oras ng pag-load, o oras ng pagtakbo. May tatlong uri ng paghabi:
  • 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."

AspectJ — Isang partikular na pagpapatupad ng paradigm ng AOP na nagpapatupad ng kakayahang magsagawa ng mga cross-cutting na gawain. Ang dokumentasyon ay matatagpuan dito .

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:
Tanner Victor Sasha
Ngayon, oras na para samantalahin ang kapangyarihan ng AOP. Ngayon ay kailangan nating lumikha ng isang aspetong file. Ang mga ito ay may dalawang uri: ang una ay may .aj file extension. Ang pangalawa ay isang ordinaryong klase na gumagamit ng mga anotasyon upang ipatupad ang mga kakayahan ng AOP . Tingnan muna natin ang file na may extension na .aj :
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:
Hi, Tanner Hi, Victor Hi, Sasha
Makikita natin na ang bawat tawag sa paraan ng printName ay nabago salamat sa isang aspeto. Ngayon tingnan natin kung ano ang magiging hitsura ng aspeto bilang isang klase ng Java na may mga anotasyon:
@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.
Ang pagpapatakbo ng pangunahing sa aspetong ito ay hindi nagbabago sa output ng console:
Hi, Tanner Hi, Victor Hi, Sasha

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:
Pagbubukas ng transaksyon... Nagsasagawa ng ilang operasyon para sa Client Tanner Pagsasara ng transaksyon...
Ngunit kung kami ay magtapon at magbubukod sa aming pamamaraan (upang gayahin ang isang nabigong operasyon):
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:
Pagbubukas ng transaksyon... Nagsasagawa ng ilang operasyon para sa Client Tanner Nabigo ang operasyon. Ibinabalik ang transaksyon...
Kaya kung ano ang natapos namin dito ay isang uri ng kakayahan sa paghawak ng error.

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:
  1. Kapag may return value ang method — if (returningValue! = Null) {
  2. Kapag walang return value — else {
Ang @AfterThrowing(value = "methodExecuting()", throwing = "exception") ay payo na ma-trigger kung sakaling magkaroon ng error, ibig sabihin, kapag naghagis ng exception ang method. At ayon dito, sa pamamagitan ng pagpapatakbo ng main , makakakuha tayo ng isang uri ng console-based na pag-log:
Matagumpay na pagpapatupad: pamamaraan — setValue, klase — Pangunahing Matagumpay na pagpapatupad: pamamaraan — getValue, klase — Pangunahing, return value — <some value> Exception thrown: method — checkValue, class — Main exception — java.lang.Exception Exception thrown: method — pangunahing, klase — Main, exception — java.lang.Exception
At dahil hindi namin pinangangasiwaan ang mga exception, makakakuha pa rin kami ng stack trace: Ano ang AOP?  Mga prinsipyo ng programming na nakatuon sa aspeto - 3Mababasa mo ang tungkol sa exception at exception handling sa mga artikulong ito: Exceptions in Java at Exceptions: catching and handling . Iyon lang para sa akin ngayon. Ngayon ay nakilala namin ang AOP , at nakita mo na ang halimaw na ito ay hindi nakakatakot gaya ng iniisip ng ilang tao. Paalam, lahat!
Mga komento
  • Sikat
  • Bago
  • Luma
Dapat kang naka-sign in upang mag-iwan ng komento
Wala pang komento ang page na ito