
एओपी लागू करना
आस्पेक्ट-ओरिएंटेड प्रोग्रामिंग को क्रॉस-कटिंग कार्यों को करने के लिए डिज़ाइन किया गया है, जो कि कोई भी कोड हो सकता है जिसे विभिन्न तरीकों से कई बार दोहराया जा सकता है, जिसे पूरी तरह से एक अलग मॉड्यूल में संरचित नहीं किया जा सकता है। तदनुसार, एओपी हमें इसे मुख्य कोड के बाहर रखने देता है और इसे लंबवत रूप से घोषित करता है। एक उदाहरण किसी एप्लिकेशन में सुरक्षा नीति का उपयोग कर रहा है। विशिष्ट रूप से, सुरक्षा किसी अनुप्रयोग के कई तत्वों से चलती है। क्या अधिक है, एप्लिकेशन की सुरक्षा नीति को एप्लिकेशन के सभी मौजूदा और नए भागों पर समान रूप से लागू किया जाना चाहिए। उसी समय, उपयोग में आने वाली सुरक्षा नीति स्वयं विकसित हो सकती है। एओपी का उपयोग करने के लिए यह सही जगह है । साथ ही, एक अन्य उदाहरण लॉगिंग है. मैन्युअल रूप से लॉगिंग कार्यात्मक जोड़ने के बजाय लॉगिंग के लिए एओपी दृष्टिकोण का उपयोग करने के कई फायदे हैं:-
लॉगिंग के लिए कोड को जोड़ना और हटाना आसान है: आपको बस इतना करना है कि किसी पहलू के कुछ कॉन्फ़िगरेशन को जोड़ना या हटाना है।
-
लॉगिंग के लिए सभी स्रोत कोड एक ही स्थान पर रखे जाते हैं, इसलिए आपको उन सभी स्थानों पर मैन्युअल रूप से खोज करने की आवश्यकता नहीं है जहां इसका उपयोग किया जाता है।
-
लॉगिंग कोड कहीं भी जोड़ा जा सकता है, चाहे विधियों और कक्षाओं में जो पहले ही लिखे जा चुके हैं या नई कार्यक्षमता में। यह कोडिंग त्रुटियों की संख्या को कम करता है।
साथ ही, डिज़ाइन कॉन्फ़िगरेशन से एक पहलू को हटाते समय, आप सुनिश्चित हो सकते हैं कि सभी ट्रेसिंग कोड चले गए हैं और कुछ भी छूटा नहीं है।
- पहलू अलग कोड हैं जिन्हें बार-बार सुधारा और उपयोग किया जा सकता है।

AOP के मूल सिद्धांत
इस विषय में आगे बढ़ने के लिए, आइए पहले AOP की मुख्य अवधारणाओं को जानें। सलाह - अतिरिक्त तर्क या कोड को एक जॉइन पॉइंट से कॉल किया जाता है। किसी ज्वाइन पॉइंट के पहले, बाद में या इसके बजाय सलाह दी जा सकती है (नीचे उनके बारे में अधिक)। संभावित प्रकार की सलाह :-
पहले - इस प्रकार की सलाह को लक्ष्य विधियों से पहले लॉन्च किया जाता है, यानी जुड़ने वाले बिंदुओं को निष्पादित किया जाता है। कक्षाओं के रूप में पहलुओं का उपयोग करते समय, हम @Before एनोटेशन का उपयोग सलाह को पहले आने के रूप में चिह्नित करने के लिए करते हैं। .aj फ़ाइलों के रूप में पहलुओं का उपयोग करते समय , यह पहले () विधि होगी ।
- के बाद - सलाह जो विधियों के निष्पादन के बाद निष्पादित की जाती है (बिंदुओं में शामिल हों) पूरी हो जाती है, दोनों सामान्य निष्पादन के साथ-साथ अपवाद फेंकते समय भी।
कक्षाओं के रूप में पहलुओं का उपयोग करते समय, हम @After एनोटेशन का उपयोग यह इंगित करने के लिए कर सकते हैं कि यह सलाह है जो बाद में आती है।
एजे फाइलों के रूप में पहलुओं का उपयोग करते समय , यह बाद () विधि है।
-
रिटर्निंग के बाद - यह सलाह केवल तभी की जाती है जब लक्ष्य विधि त्रुटियों के बिना सामान्य रूप से समाप्त हो जाती है।
जब पहलुओं को कक्षाओं के रूप में दर्शाया जाता है, तो हम सफल समापन के बाद सलाह को क्रियान्वित करने के रूप में चिह्नित करने के लिए @AfterReturning एनोटेशन का उपयोग कर सकते हैं।
.aj फ़ाइलों के रूप में पहलुओं का उपयोग करते समय , यह आफ्टर() रिटर्निंग (ऑब्जेक्ट ओब्ज) विधि होगी ।
-
फेंकने के बाद - यह सलाह ऐसे उदाहरणों के लिए अभिप्रेत है जब एक विधि, जो कि, बिंदु से जुड़ती है, एक अपवाद फेंकती है। हम इस सलाह का उपयोग कुछ प्रकार के विफल निष्पादन को संभालने के लिए कर सकते हैं (उदाहरण के लिए, संपूर्ण लेन-देन वापस करने के लिए या आवश्यक ट्रेस स्तर के साथ लॉग इन करने के लिए)।
वर्ग पहलुओं के लिए, @AfterThrowing एनोटेशन का उपयोग यह इंगित करने के लिए किया जाता है कि अपवाद फेंकने के बाद इस सलाह का उपयोग किया जाता है।
एजे फाइलों के रूप में पहलुओं का उपयोग करते समय , यह बाद () फेंकने (अपवाद ई) विधि होगी ।
-
चारों ओर - शायद सबसे महत्वपूर्ण प्रकार की सलाह में से एक। यह एक विधि को घेरता है, अर्थात, एक जुड़ने वाला बिंदु जिसका हम उपयोग कर सकते हैं, उदाहरण के लिए, यह चुनने के लिए कि किसी दिए गए जुड़ाव बिंदु विधि को निष्पादित करना है या नहीं।
आप सलाह कोड लिख सकते हैं जो ज्वाइन पॉइंट मेथड के निष्पादित होने से पहले और बाद में चलता है।
ज्वाइन पॉइंट मेथड को कॉल करने के लिए आस-पास की सलाह जिम्मेदार है और अगर मेथड कुछ रिटर्न करता है तो रिटर्न वैल्यू। दूसरे शब्दों में, इस सलाह में, आप बिना कॉल किए किसी लक्ष्य विधि के संचालन का अनुकरण कर सकते हैं, और जो कुछ भी आप चाहते हैं उसे रिटर्न परिणाम के रूप में वापस कर सकते हैं।
वर्गों के रूप में दिए गए पहलुओं को देखते हुए, हम सलाह देने के लिए @Around एनोटेशन का उपयोग करते हैं जो एक जॉइन पॉइंट को लपेटता है। .aj फ़ाइलों के रूप में पहलुओं का उपयोग करते समय , यह विधि चारों ओर () विधि होगी ।
-
संकलन-समय की बुनाई - यदि आपके पास पहलू का स्रोत कोड और कोड है जहां आप पहलू का उपयोग करते हैं, तो आप स्रोत कोड और पहलू को सीधे AspectJ संकलक का उपयोग करके संकलित कर सकते हैं;
-
संकलन के बाद की बुनाई (बाइनरी बुनाई) - यदि आप कोड में पहलुओं को बुनने के लिए स्रोत कोड परिवर्तनों का उपयोग नहीं कर सकते हैं या नहीं करना चाहते हैं, तो आप पहले से संकलित कक्षाएं या जार फाइलें ले सकते हैं और उनमें पहलुओं को इंजेक्ट कर सकते हैं;
-
लोड-टाइम बुनाई - यह केवल बाइनरी बुनाई है जो तब तक विलंबित होती है जब तक कि क्लास लोडर क्लास फाइल को लोड नहीं करता है और जेवीएम के लिए क्लास को परिभाषित करता है।
इसका समर्थन करने के लिए एक या अधिक बुनाई वर्ग लोडर की आवश्यकता होती है। वे या तो रनटाइम द्वारा स्पष्ट रूप से प्रदान किए जाते हैं या "वीविंग एजेंट" द्वारा सक्रिय किए जाते हैं।
जावा में उदाहरण
अगला, AOP की बेहतर समझ के लिए , हम छोटे "हैलो वर्ल्ड"-शैली के उदाहरण देखेंगे। बल्ले के अधिकार, मैं ध्यान दूंगा कि हमारे उदाहरण संकलन-समय की बुनाई का उपयोग करेंगे । सबसे पहले, हमें अपनी pom.xml फ़ाइल में निम्नलिखित डिपेंडेंसी को जोड़ना होगा :
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
एक नियम के रूप में, विशेष ajc संकलक यह है कि हम पहलुओं का उपयोग कैसे करते हैं। IntelliJ IDEA में यह डिफ़ॉल्ट रूप से शामिल नहीं है, इसलिए इसे एप्लिकेशन कंपाइलर के रूप में चुनते समय, आपको 5168 75 AspectJ वितरण के लिए पथ निर्दिष्ट करना होगा। यह पहला तरीका था। दूसरा, जो मैंने उपयोग किया है, निम्नलिखित प्लगइन को 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>
इसके बाद, मेवेन से पुनः आयात करना और एमवीएन क्लीन कंपाइल चलाना एक अच्छा विचार है । अब सीधे उदाहरणों पर चलते हैं।
उदाहरण संख्या 1
चलिए एक मुख्य वर्ग बनाते हैं। इसमें, हमारे पास एक प्रवेश बिंदु और एक विधि होगी जो कंसोल पर पास किए गए नाम को प्रिंट करती है:
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);
}
}
यहाँ कुछ भी जटिल नहीं है। हमने एक नाम पारित किया और इसे कंसोल पर प्रदर्शित किया। यदि हम अभी प्रोग्राम चलाते हैं, तो हम कंसोल पर निम्नलिखित देखेंगे:
public aspect GreetingAspect {
pointcut greeting() : execution(* Main.printName(..));
before() : greeting() {
System.out.print("Hi, ");
}
}
यह फाइल एक क्लास की तरह है। आइए देखें कि यहां क्या हो रहा है: पॉइंटकट जॉइन पॉइंट्स का एक सेट है; ग्रीटिंग () इस पॉइंटकट का नाम है; : निष्पादन Main.printName(...) विधि के सभी ( * ) कॉल के निष्पादन के दौरान इसे लागू करने का संकेत देता है । इसके बाद एक विशिष्ट सलाह आती है - पहले () - जिसे लक्ष्य विधि कहे जाने से पहले निष्पादित किया जाता है। : ग्रीटिंग () वह कटपॉइंट है जिसका यह सलाह जवाब देती है। ठीक है, और नीचे हम विधि के शरीर को ही देखते हैं, जो कि जावा भाषा में लिखा गया है, जिसे हम समझते हैं। जब हम इस पहलू के साथ मुख्य चलाते हैं , तो हमें यह कंसोल आउटपुट मिलेगा:
@Aspect
public class GreetingAspect{
@Pointcut("execution(* Main.printName(String))")
public void greeting() {
}
@Before("greeting()")
public void beforeAdvice() {
System.out.print("Hi, ");
}
}
.aj पहलू फ़ाइल के बाद , यहाँ सब कुछ और अधिक स्पष्ट हो जाता है:
- @Aspect इंगित करता है कि यह वर्ग एक पहलू है;
- @Pointcut("execution(* Main.printName(String))") वह कटपॉइंट है जो Main.printName के सभी कॉल के लिए एक इनपुट तर्क के साथ ट्रिगर होता है जिसका प्रकार String है ;
- @Before("greeting()") सलाह है जो ग्रीटिंग() कटपॉइंट में निर्दिष्ट कोड को कॉल करने से पहले लागू की जाती है ।
उदाहरण संख्या 2
मान लीजिए कि हमारे पास कुछ विधि है जो ग्राहकों के लिए कुछ संचालन करती है, और हम इस विधि को मुख्य से कहते हैं :
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);
}
}
आइए "छद्म लेनदेन" बनाने के लिए @Around एनोटेशन का उपयोग करें:
@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...");
}
}
}
ProceedingJoinPoint ऑब्जेक्ट की आगे बढ़ने की विधि के साथ , हम सलाह में इसका स्थान निर्धारित करने के लिए रैपिंग विधि कहते हैं। इसलिए, उपरोक्त विधि में कोड joinPoint.proceed(); बिफोर है , जबकि इसके नीचे का कोड आफ्टर है । यदि हम main चलाते हैं , तो हमें यह कंसोल में मिलता है:
public static void performSomeOperation(String clientName) throws Exception {
System.out.println("Performing some operations for Client " + clientName);
throw new Exception();
}
तब हमें यह कंसोल आउटपुट मिलता है:
उदाहरण संख्या 3
हमारे अगले उदाहरण में, कंसोल में लॉग इन करने जैसा कुछ करते हैं। सबसे पहले, मुख्य पर एक नज़र डालें , जहाँ हमने कुछ छद्म व्यावसायिक तर्क जोड़े हैं:
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();
}
}
}
main में , हम वैल्यू इंस्टेंस वेरिएबल को वैल्यू असाइन करने के लिए setValue का उपयोग करते हैं। फिर हम मान प्राप्त करने के लिए getValue का उपयोग करते हैं, और फिर हम यह देखने के लिए checkValue कहते हैं कि यह 10 वर्णों से अधिक लंबा है या नहीं। यदि ऐसा है, तो एक अपवाद फेंका जाएगा। अब आइए उस पहलू को देखें जिसका उपयोग हम विधियों के कार्य को लॉग करने के लिए करेंगे:
@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);
}
}
यहाँ क्या चल रहा है? @Pointcut("निष्पादन (* *(..))") सभी विधियों की सभी कॉलों में शामिल हो जाएगा। @AfterReturning(value = "methodExecuting()", returning = "returningValue") वह सलाह है जिसे लक्ष्य विधि के सफल निष्पादन के बाद क्रियान्वित किया जाएगा। हमारे यहां दो मामले हैं:
- जब विधि का रिटर्न मान होता है — if (returningValue! = Null) {
- जब कोई रिटर्न वैल्यू नहीं है - और {

GO TO FULL VERSION