JavaRush /Java Blog /Random-TL /Ano ang AOP? Aspect-Oriented Programming Basics

Ano ang AOP? Aspect-Oriented Programming Basics

Nai-publish sa grupo
Hello guys! 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 isa sa mga konseptong ito - AOP, o aspect-oriented programming . Ano ang AOP?  Mga Pundamental ng Aspect-Oriented Programming - 1Ito ay hindi isang madaling paksa at hindi madalas na direktang ginagamit, ngunit maraming mga balangkas at teknolohiya ang gumagamit nito sa ilalim ng hood. At siyempre, minsan sa mga panayam ay maaaring hilingin sa iyo na sabihin sa iyo sa pangkalahatan kung anong uri ng hayop ito at kung saan ito magagamit. Kaya tingnan natin ang mga pangunahing konsepto at ilang simpleng halimbawa ng AOP sa Java . Ano ang AOP?  Mga Batayan ng Aspect-Oriented Programming - 2Kaya, ang AOP - aspect-oriented programming - ay isang paradigm na naglalayong pataasin ang modularity ng iba't ibang bahagi ng isang application sa pamamagitan ng paghihiwalay ng mga cross-cutting na alalahanin. Upang gawin ito, ang karagdagang pag-uugali ay idinagdag sa umiiral na code, nang hindi binabago ang orihinal na code. Sa madaling salita, tila nagsabit kami ng karagdagang pag-andar sa itaas ng mga pamamaraan at klase nang hindi gumagawa ng mga pagbabago sa binagong code. Bakit kailangan ito? Maaga o huli ay makakarating tayo sa konklusyon na ang karaniwang object-oriented na diskarte ay hindi palaging epektibong malulutas ang ilang mga problema. Sa ganoong sandali, sumagip ang AOP at nagbibigay sa amin ng mga karagdagang tool para buuin ang application. At ang mga karagdagang tool ay nangangahulugan ng pagtaas ng kakayahang umangkop sa pag-unlad, salamat sa kung saan mayroong higit pang mga pagpipilian para sa paglutas ng isang partikular na problema.

Paglalapat ng AOP

Ang programming na nakatuon sa aspeto ay idinisenyo upang malutas ang mga problema sa cross-cutting, na maaaring maging anumang code na paulit-ulit nang maraming beses sa iba't ibang paraan, na hindi maaaring ganap na ibalangkas sa isang hiwalay na module. Alinsunod dito, sa AOP maaari nating iwanan ito sa labas ng pangunahing code at tukuyin ito nang patayo. Ang isang halimbawa ay ang aplikasyon ng isang patakaran sa seguridad sa isang aplikasyon. Karaniwan, pinuputol ng seguridad ang maraming elemento ng isang application. Bukod dito, ang patakaran sa seguridad ng aplikasyon ay dapat na mailapat nang pantay-pantay sa lahat ng umiiral at bagong bahagi ng aplikasyon. Kasabay nito, ang patakaran sa seguridad na ginamit ay maaaring mag-evolve mismo. Dito maaaring magamit ang paggamit ng AOP . Gayundin ang isa pang halimbawa ay ang pag - log . Mayroong ilang mga pakinabang sa paggamit ng isang AOP na diskarte sa pag-log kumpara sa manu-manong pagpasok ng pag-log:
  1. Madaling ipatupad at alisin ang logging code: kailangan mo lang magdagdag o mag-alis ng ilang configuration ng ilang aspeto.
  2. Ang lahat ng source code para sa pag-log ay naka-imbak sa isang lugar at hindi na kailangang manual na mahanap ang lahat ng mga lugar ng paggamit.
  3. Ang code na inilaan para sa pag-log ay maaaring idagdag kahit saan, ito man ay nakasulat na mga pamamaraan at klase o bagong functionality. Binabawasan nito ang bilang ng mga error sa developer.
    Gayundin, kapag nag-alis ka ng isang aspeto mula sa isang configuration ng disenyo, maaari kang maging ganap na sigurado na ang lahat ng trace code ay aalisin at walang nawawala.
  4. Ang mga aspeto ay stand-alone na code na maaaring gamitin muli at paulit-ulit na pahusayin.
Ano ang AOP?  Mga Pundamental ng Aspect-Oriented Programming - 3Ginagamit din ang AOP para sa exception na paghawak, pag-cache, at pag-alis ng ilang functionality upang gawin itong magagamit muli.

Pangunahing konsepto ng AOP

Upang makasulong pa sa pagsusuri ng paksa, kilalanin muna natin ang mga pangunahing konsepto ng AOP. Ang payo ay karagdagang lohika, code, na tinatawag mula sa punto ng koneksyon. Ang payo ay maaaring isagawa bago, pagkatapos o sa halip ng punto ng koneksyon (higit pa sa mga ito sa ibaba). Mga posibleng uri ng payo :
  1. Bago - ang payo ng ganitong uri ay inilunsad bago ang pagpapatupad ng mga target na pamamaraan - mga punto ng koneksyon. Kapag gumagamit ng mga aspeto bilang mga klase, kinukuha namin ang @Before annotation upang markahan ang uri ng payo bilang nauna. Kapag gumagamit ng mga aspeto bilang .aj file , ito ang magiging before() method .
  2. Pagkatapos (Pagkatapos) - payo na isinasagawa pagkatapos makumpleto ang pagpapatupad ng mga pamamaraan - mga punto ng koneksyon, kapwa sa mga normal na kaso at kapag ang isang pagbubukod ay itinapon.
    Kapag gumagamit ng mga aspeto bilang mga klase, maaari naming gamitin ang @After annotation upang ipahiwatig na ito ay isang tip na darating pagkatapos.
    Kapag gumagamit ng mga aspeto bilang .aj file , ito ang magiging after() na paraan .
  3. Pagkatapos ng Pagbabalik - ang mga tip na ito ay isinasagawa lamang kung ang target na paraan ay gumagana 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 naisakatuparan sa matagumpay na pagkumpleto.
    Kapag gumagamit ng mga aspeto bilang mga .aj file, ito ang magiging after() method na bumabalik (Object obj) .
  4. Pagkatapos ng Paghahagis - ang ganitong uri ng payo ay inilaan para sa mga kasong iyon kapag ang isang pamamaraan, iyon ay, isang punto ng koneksyon, ay nagtatapon ng isang pagbubukod. Magagamit namin ang payong ito para sa ilang paghawak sa nabigong pagpapatupad (halimbawa, pagbabalik ng buong transaksyon o pag-log gamit ang kinakailangang antas ng bakas).
    Para sa mga klase ng aspeto, ang @AfterThrowing annotation ay ginagamit upang isaad na ang payo na ito ay ginagamit pagkatapos na maihagis ang isang exception.
    Kapag gumagamit ng mga aspeto sa anyo ng mga .aj file , ito ang magiging paraan - pagkatapos() paghagis (Exception e) .
  5. Ang paligid ay marahil ang isa sa pinakamahalagang uri ng payo na pumapalibot sa isang paraan, iyon ay, isang punto ng koneksyon, kung saan maaari nating, halimbawa, piliin kung isasagawa ang isang ibinigay na paraan ng punto ng koneksyon o hindi.
    Maaari kang magsulat ng code ng payo na tumatakbo bago at pagkatapos isagawa ang paraan ng join point.
    Kasama sa mga responsibilidad ng payo ang pagtawag sa paraan ng pagsali sa punto at pagbabalik ng mga halaga kung ang pamamaraan ay nagbabalik ng isang bagay. Iyon ay, sa tip na ito maaari mo lamang gayahin ang pagpapatakbo ng target na paraan nang hindi ito tinatawag, at ibalik ang iyong sarili bilang isang resulta.
    Para sa mga aspeto sa anyo ng mga klase, ginagamit namin ang @Around na anotasyon upang lumikha ng mga tip na bumabalot sa punto ng koneksyon. Kapag gumagamit ng mga aspeto bilang .aj file , ito ang magiging around() method .
Join point - isang punto sa isang executing program (pagtawag ng paraan, paggawa ng object, pag-access ng variable) kung saan dapat ilapat ang payo. Sa madaling salita, ito ay isang uri ng regular na expression, sa tulong ng kung aling mga lugar para sa pagpapakilala ng code (mga lugar para sa paglalapat ng mga tip) ay matatagpuan. Ang pointcut ay isang hanay ng mga punto ng koneksyon . Tinutukoy ng hiwa kung ang isang ibinigay na punto ng koneksyon ay umaangkop sa isang ibinigay na tip. Ang aspeto ay isang module o klase na nagpapatupad ng end-to-end functionality. Binabago ng isang aspeto ang pag-uugali ng natitirang code sa pamamagitan ng paglalapat ng payo sa mga punto ng pagsali na tinukoy ng ilang slice . Sa madaling salita, ito ay isang kumbinasyon ng mga tip at mga punto ng koneksyon. Panimula - pagpapalit ng istruktura ng isang klase at/o pagpapalit ng inheritance hierarchy upang magdagdag ng aspetong functionality sa foreign code. Ang target ay ang bagay kung saan ilalapat ang payo. Ang paghabi ay ang proseso ng pag-uugnay ng mga aspeto sa iba pang mga bagay upang lumikha ng mga inirerekomendang 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 isang aspeto at ang code kung saan mo ginagamit ang mga aspeto, maaari mong direktang i-compile ang source code at aspeto gamit ang AspectJ compiler;
  • post-compilation weaving (binary weaving) - kung hindi mo kaya o ayaw mong gumamit ng mga pagbabago sa source code upang ihabi ang mga aspeto sa iyong code, maaari kang kumuha ng mga pinagsama-samang klase o garapon at mag-inject ng mga aspeto;
  • Ang load-time weaving ay binary weaving na ipinagpaliban hanggang sa i-load ng class loader ang class file at tukuyin ang klase para sa JVM.
    Para suportahan ito, kailangan ang isa o higit pang "weave class loader". Ang mga ito ay alinman sa tahasang ibinigay ng runtime o na-activate ng "weaving agent."
Ang AspectJ ay isang partikular na pagpapatupad ng mga paradigma ng AOP na nagpapatupad ng kakayahang lutasin ang mga problema sa cross-cutting. 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 antas ng Hello World. Ano ang AOP?  Mga Pundamental ng Aspect-Oriented Programming - 4Hayaan akong agad na tandaan na sa aming mga halimbawa ay gagamitin namin ang compile-time weaving . Una kailangan naming idagdag ang sumusunod na dependency sa aming pom.xml :
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.9.5</version>
</dependency>
Bilang isang tuntunin, isang espesyal na Ajs compiler ang ginagamit upang gumamit ng mga aspeto . Ang IntelliJ IDEA ay wala nito bilang default, kaya kapag pinipili ito bilang isang compiler ng application kailangan mong tukuyin ang landas patungo sa pamamahagi ng AspectJ . Maaari kang magbasa nang higit pa tungkol sa paraan ng pagpili ng Ajs bilang isang compiler sa pahinang ito. Ito ang unang paraan, at ang pangalawa (na ginamit ko) ay idagdag ang sumusunod na plugin sa pom.xml :
<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, ipinapayong muling mag-import mula sa Maven at patakbuhin ang mvn clean compile . Ngayon ay lumipat tayo sa mga halimbawa.

Halimbawa Blg. 1

Gumawa tayo ng Pangunahing klase . Sa loob nito magkakaroon kami ng isang launch point at isang paraan na nagpi-print ng mga pangalan na ipinasa dito sa console:
public class Main {

  public static void main(String[] args) {
  printName("Толя");
  printName("Вова");
  printName("Sasha");
  }

  public static void printName(String name) {
     System.out.println(name);
  }
}
Walang kumplikado: ipinasa nila ang pangalan at ipinakita ito sa console. Kung patakbuhin natin ito ngayon, ipapakita ng console ang:
Tolya Vova Sasha
Well, oras na para samantalahin ang kapangyarihan ng AOP. Ngayon kailangan naming lumikha ng isang file- aspect . May dalawang uri ang mga ito: ang una ay isang file na may extension na .aj , ang pangalawa ay isang regular na klase na nagpapatupad ng mga kakayahan ng AOP gamit ang mga anotasyon. Tingnan muna natin ang isang file na may extension na .aj :
public aspect GreetingAspect {

  pointcut greeting() : execution(* Main.printName(..));

  before() : greeting() {
     System.out.print("Привет ");
  }
}
Ang file na ito ay medyo katulad ng isang klase. Alamin natin kung ano ang nangyayari dito: pointcut - isang hiwa o isang set ng mga punto ng koneksyon; greeting() — ang pangalan ng slice na ito; : execution - kapag nag-execute * - all, call - Main.printName(..) - ang paraang ito. Susunod ang tiyak na payo - before() - na naisakatuparan bago tawagin ang target na paraan, : greeting() - ang hiwa kung saan tumutugon ang payo na ito, at sa ibaba ay makikita natin ang katawan ng mismong pamamaraan, na nakasulat sa Java wikang ating naiintindihan. Kapag nagpatakbo kami ng pangunahing kasama ang aspetong ito, makukuha namin ang sumusunod na output sa console:
Hello Tolya Hello Vova Hello Sasha
Makikita natin na ang bawat tawag sa paraan ng printName ay binago ng isang aspeto. Ngayon tingnan natin kung ano ang magiging hitsura ng aspeto, ngunit 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("Привет ");
  }
}
Pagkatapos ng .aj aspect file , mas malinaw ang lahat:
  • Ang @Aspect ay nagsasaad na ang ibinigay na klase ay isang aspeto;
  • Ang @Pointcut("execution(* Main.printName(String))") ay isang cut point na nagpapagana sa lahat ng tawag sa Main.printName na may papasok na argument na may uri ng String ;
  • @Before("greeting()") - payo na inilapat bago tawagan ang code na inilarawan sa greeting() cut point .
Ang pagpapatakbo ng pangunahing gamit ang aspetong ito ay hindi magbabago sa output ng console:
Hello Tolya Hello Vova Hello Sasha

Halimbawa Blg. 2

Sabihin nating mayroon kaming ilang pamamaraan na nagsasagawa ng ilang operasyon para sa mga kliyente at tawagan ang pamamaraang ito mula sa pangunahing :
public class Main {

  public static void main(String[] args) {
  makeSomeOperation("Толя");
  }

  public static void makeSomeOperation(String clientName) {
     System.out.println("Выполнение некоторых операций для клиента - " + clientName);
  }
}
Gamit ang @Around annotation , gawin natin ang isang bagay tulad ng isang "pseudo-transaction":
@Aspect
public class TransactionAspect{

  @Pointcut("execution(* Main.makeSomeOperation(String))")
  public void executeOperation() {
  }

  @Around(value = "executeOperation()")
  public void beforeAdvice(ProceedingJoinPoint joinPoint) {
     System.out.println("Открытие транзакции...");
     try {
        joinPoint.proceed();
        System.out.println("Закрытие транзакции....");
     }
     catch (Throwable throwable) {
        System.out.println("Операция не удалась, откат транзакции...");
     }
  }
  }
Gamit ang proceed method ng ProceedingJoinPoint object , tinatawag namin ang paraan ng wrapper upang matukoy ang lugar nito sa board at, nang naaayon, ang code sa paraan sa itaas joinPoint.proceed(); - ito ay Bago , na nasa ibaba - Pagkatapos . Kung tatakbo tayo sa pangunahing makakakuha tayo sa console:
Pagbubukas ng transaksyon... Nagsasagawa ng ilang operasyon para sa kliyente - Tolya Pagsasara ng transaksyon....
Kung magdaragdag kami ng exception throw sa aming pamamaraan (biglang nabigo ang operasyon):
public static void makeSomeOperation(String clientName)throws Exception {
  System.out.println("Выполнение некоторых операций для клиента - " + clientName);
  throw new Exception();
}
Pagkatapos ay makukuha natin ang output sa console:
Pagbubukas ng transaksyon... Nagsasagawa ng ilang operasyon para sa kliyente - Tolya Nabigo ang operasyon, na-roll back ang transaksyon...
Ito ay naging isang pseudo-processing ng pagkabigo.

Halimbawa Blg. 3

Bilang susunod na halimbawa, gawin natin ang isang bagay tulad ng pag-log in sa console. Una, tingnan natin ang Main , kung saan nangyayari ang ating pseudo business logic:
public class Main {
  private String value;

  public static void main(String[] args) throws Exception {
     Main main = new Main();
     main.setValue("<некоторое meaning>");
     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 main , gamit ang setValue, itatakda namin ang halaga ng panloob na variable - value , pagkatapos gamit ang getValue, kukunin namin ang halagang ito at sa checkValue ay susuriin namin kung ang halagang ito ay mas mahaba sa 10 character. Kung oo, isang pagbubukod ang itatapon. Ngayon tingnan natin ang aspeto kung saan itatala natin ang pagpapatakbo 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("Успешно выполнен метод - %s, класса- %s, с результатом выполнения - %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName(),
              returningValue);
     }
     else {
        System.out.printf("Успешно выполнен метод - %s, класса- %s\n",
              joinPoint.getSignature().getName(),
              joinPoint.getSourceLocation().getWithinType().getName());
     }
  }

  @AfterThrowing(value = "methodExecuting()", throwing = "exception")
  public void recordFailedExecution(JoinPoint joinPoint, Exception exception) {
     System.out.printf("Метод - %s, класса- %s, был аварийно завершен с исключением - %s\n",
           joinPoint.getSignature().getName(),
           joinPoint.getSourceLocation().getWithinType().getName(),
           exception);
  }
}
Anong nangyayari dito? @Pointcut("execution(* *(..))") - kokonekta sa lahat ng tawag sa lahat ng pamamaraan; @AfterReturning(value = "methodExecuting()", returning = "returningValue") - payo na isasagawa pagkatapos matagumpay na makumpleto ang target na paraan. Mayroon kaming dalawang kaso dito:
  1. Kapag ang isang paraan ay may return value kung (returningValue != null) {
  2. Kapag walang ibang return value {
@AfterThrowing(value = "methodExecuting()", throwing = "exception") - payo na ma-trigger sa kaso ng isang error, iyon ay, kapag ang isang exception ay itinapon mula sa pamamaraan. At ayon dito, sa pamamagitan ng pagpapatakbo main , makakakuha tayo ng isang uri ng pag-log in sa console:
Ang pamamaraan - setValue, ng klase - Main ay matagumpay na naisakatuparan. Ang pamamaraan - getValue, ng klase - Main, ay matagumpay na naisakatuparan, na may resulta ng pagpapatupad - <some value> Ang pamamaraan - checkValue, ng klase - Main, ay tinapos nang abnormal na may exception - java.lang.Exception Method - main, class-Main, nag-crash na may exception - java.lang.Exception
Well, dahil hindi namin pinangangasiwaan ang exception, makukuha rin namin ang stacktrace nito: Ano ang AOP?  Mga Pundamental ng Aspect-Oriented Programming - 5Mababasa mo ang tungkol sa mga exception at ang kanilang pangangasiwa sa mga artikulong ito: Exceptions in Java and Exceptions and their handling . Iyon lang para sa akin ngayon. Ngayon ay nakilala natin ang AOP , at makikita mo na ang halimaw na ito ay hindi nakakatakot gaya ng ipininta. Paalam sa lahat!Ano ang AOP?  Mga Batayan ng Aspect-Oriented Programming - 6
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION