AOP लागू करत आहे
आस्पेक्ट-ओरिएंटेड प्रोग्रामिंग क्रॉस-कटिंग कार्ये करण्यासाठी डिझाइन केलेले आहे, जे विविध पद्धतींनी अनेक वेळा पुनरावृत्ती होऊ शकणारा कोणताही कोड असू शकतो, ज्याची पूर्णपणे स्वतंत्र मॉड्यूलमध्ये रचना केली जाऊ शकत नाही. त्यानुसार, AOP आम्हाला हे मुख्य कोडच्या बाहेर ठेवू देते आणि ते अनुलंब घोषित करू देते. अनुप्रयोगामध्ये सुरक्षा धोरण वापरणे हे एक उदाहरण आहे. सामान्यतः, सुरक्षा अनुप्रयोगाच्या अनेक घटकांमधून चालते. इतकेच काय, ऍप्लिकेशनचे सुरक्षा धोरण ऍप्लिकेशनच्या सर्व विद्यमान आणि नवीन भागांना समान रीतीने लागू केले जावे. त्याच वेळी, वापरात असलेले सुरक्षा धोरण स्वतःच विकसित होऊ शकते. हे AOP वापरण्यासाठी योग्य ठिकाण आहे . तसेच, दुसरे उदाहरण लॉगिंग आहे. लॉगिंग फंक्शनल मॅन्युअली जोडण्याऐवजी लॉगिंगसाठी AOP दृष्टिकोन वापरण्याचे अनेक फायदे आहेत:-
लॉगिंगसाठी कोड जोडणे आणि काढणे सोपे आहे: तुम्हाला फक्त काही पैलूंची दोन कॉन्फिगरेशन जोडणे किंवा काढून टाकणे आवश्यक आहे.
-
लॉगिंगसाठी सर्व स्त्रोत कोड एकाच ठिकाणी ठेवला आहे, त्यामुळे तुम्हाला तो वापरल्या जाणार्या सर्व ठिकाणी व्यक्तिचलितपणे शोधण्याची आवश्यकता नाही.
-
लॉगिंग कोड कुठेही जोडला जाऊ शकतो, मग ते आधीपासून लिहिलेल्या पद्धती आणि वर्गांमध्ये किंवा नवीन कार्यक्षमतेमध्ये. हे कोडिंग त्रुटींची संख्या कमी करते.
तसेच, डिझाईन कॉन्फिगरेशनमधून पैलू काढून टाकताना, तुम्ही खात्री बाळगू शकता की सर्व ट्रेसिंग कोड गेले आहेत आणि काहीही चुकले नाही.
- पैलू हे वेगळे कोड आहेत जे सुधारले जाऊ शकतात आणि पुन्हा पुन्हा वापरले जाऊ शकतात.
AOP ची मूलभूत तत्त्वे
या विषयात पुढे जाण्यासाठी, प्रथम AOP च्या मुख्य संकल्पना जाणून घेऊया. सल्ला - अतिरिक्त लॉजिक किंवा कोड जॉईन पॉइंटवरून कॉल केला जातो. जॉइन पॉइंटच्या आधी, नंतर किंवा त्याऐवजी सल्ला दिला जाऊ शकतो (खाली त्यांच्याबद्दल अधिक). संभाव्य प्रकारचे सल्लाः-
आधी — लक्ष्य पद्धती, म्हणजे जॉईन पॉइंट्स, कार्यान्वित होण्यापूर्वी या प्रकारचा सल्ला सुरू केला जातो. वर्ग म्हणून पैलू वापरताना, आम्ही सल्ले आधी येत असल्याचे चिन्हांकित करण्यासाठी @Before भाष्य वापरतो. .aj फाइल्स म्हणून पैलू वापरताना , ही before() पद्धत असेल .
- नंतर — पद्धती अंमलात आणल्यानंतर (जॉईन पॉईंट्स) पूर्ण झाल्यानंतर, सामान्य अंमलबजावणीमध्ये तसेच अपवाद फेकताना दोन्हीपैकी सल्ला दिला जातो.
वर्ग म्हणून पैलू वापरताना, आम्ही @After भाष्य वापरू शकतो की हा सल्ला नंतर येतो.
.aj फाइल्स म्हणून पैलू वापरताना , ही after() पद्धत आहे.
-
परत आल्यानंतर — हा सल्ला केवळ तेव्हाच केला जातो जेव्हा लक्ष्य पद्धत सामान्यपणे पूर्ण होते, त्रुटीशिवाय.
जेव्हा पैलू वर्ग म्हणून दर्शविले जातात, तेव्हा आम्ही @AfterReturning भाष्य वापरून सल्ला यशस्वीरित्या पूर्ण झाल्यानंतर कार्यान्वित होत असल्याचे चिन्हांकित करू शकतो.
.aj फाइल्स म्हणून पैलू वापरताना , ही after() रिटर्निंग (ऑब्जेक्ट ऑब्जेक्ट) पद्धत असेल .
-
फेकल्यानंतर - हा सल्ला अशा उदाहरणांसाठी आहे जेव्हा एखादी पद्धत, म्हणजेच जॉईन पॉइंट, अपवाद फेकते. आम्ही या सल्ल्याचा वापर काही प्रकारचे अयशस्वी अंमलबजावणी हाताळण्यासाठी करू शकतो (उदाहरणार्थ, संपूर्ण व्यवहार परत करण्यासाठी किंवा आवश्यक ट्रेस पातळीसह लॉग इन करण्यासाठी).
वर्ग पैलूंसाठी, @AfterThrowing भाष्य हे सूचित करण्यासाठी वापरले जाते की हा सल्ला अपवाद टाकल्यानंतर वापरला जातो.
.aj फाइल्स म्हणून पैलू वापरताना , ही after() फेकण्याची (अपवाद e) पद्धत असेल .
-
आजूबाजूला - कदाचित सल्ल्याचा सर्वात महत्वाचा प्रकार. हे एका पद्धतीभोवती असते, म्हणजे, जॉईन पॉइंट ज्यासाठी आपण वापरू शकतो, उदाहरणार्थ, दिलेल्या जॉईन पॉईंट पद्धतीचे कार्य करायचे की नाही ते निवडा.
जॉइन पॉइंट पद्धत अंमलात आणण्यापूर्वी आणि नंतर चालणारा सल्ला कोड तुम्ही लिहू शकता.
जॉईन पॉइंट मेथडला कॉल करण्यासाठी आणि जर मेथडने काही रिटर्न केले तर रिटर्न व्हॅल्यूजसाठी आसपासचा सल्ला जबाबदार असतो. दुसऱ्या शब्दांत, या सल्ल्यानुसार, तुम्ही कॉल न करता लक्ष्य पद्धतीच्या ऑपरेशनचे अनुकरण करू शकता आणि रिटर्न रिझल्ट म्हणून तुम्हाला हवे ते परत करू शकता.
वर्ग म्हणून दिलेले पैलू, आम्ही @Around भाष्य वापरून सल्ला तयार करतो जो जॉईन पॉइंट गुंडाळतो. .aj फाइल्सच्या स्वरूपात पैलू वापरताना , ही पद्धत सुमारे() पद्धत असेल .
-
संकलित-वेळ विणकाम — जर तुमच्याकडे पैलूचा स्त्रोत कोड आणि कोड असेल जेथे तुम्ही पैलू वापरता, तर तुम्ही थेट AspectJ कंपाइलर वापरून स्त्रोत कोड आणि पैलू संकलित करू शकता;
-
पोस्ट-कंपाइल विणकाम (बायनरी विणकाम) — जर तुम्ही कोडमध्ये पैलू विणण्यासाठी सोर्स कोड ट्रान्सफॉर्मेशन वापरू शकत नसाल किंवा करू इच्छित नसाल, तर तुम्ही पूर्वी संकलित केलेले क्लासेस किंवा जार फाइल्स घेऊ शकता आणि त्यामध्ये पैलू इंजेक्ट करू शकता;
-
लोड-टाइम विणकाम - हे फक्त बायनरी विणकाम आहे जे क्लासलोडरने क्लास फाइल लोड करेपर्यंत आणि JVM साठी क्लास परिभाषित करेपर्यंत विलंब होतो.
याचे समर्थन करण्यासाठी एक किंवा अधिक विणकाम वर्ग लोडर आवश्यक आहेत. ते एकतर रनटाइमद्वारे स्पष्टपणे प्रदान केले जातात किंवा "विव्हिंग एजंट" द्वारे सक्रिय केले जातात.
Java मधील उदाहरणे
पुढे, AOP च्या चांगल्या प्रकारे समजून घेण्यासाठी , आम्ही "हॅलो वर्ल्ड"-शैलीची छोटी उदाहरणे पाहू. बॅटच्या उजवीकडे, मी लक्षात घेईन की आमची उदाहरणे compile-time weaving वापरतील . प्रथम, आम्हाला आमच्या pom.xml फाइलमध्ये खालील अवलंबित्व जोडण्याची आवश्यकता आहे :
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
नियमानुसार, स्पेशल एजेसी कंपाइलर म्हणजे आम्ही पैलू कसे वापरतो. 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>
यानंतर, Maven वरून पुन्हा आयात करणे आणि mvn clean compile चालवणे चांगली कल्पना आहे . आता थेट उदाहरणांकडे जाऊया.
उदाहरण क्रमांक १
चला मुख्य वर्ग तयार करू. त्यामध्ये, आमच्याकडे एक एंट्री पॉईंट आणि एक पद्धत असेल जी कन्सोलवर पास केलेले नाव मुद्रित करते:
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, ");
}
}
ही फाईल वर्गासारखी आहे. येथे काय घडत आहे ते पाहू या: पॉइंटकट जॉइन पॉइंट्सचा एक संच आहे; greeting() हे या पॉइंटकटचे नाव आहे; : execution हे Main.printName(...) पद्धतीच्या सर्व ( * ) कॉलच्या अंमलबजावणीदरम्यान लागू करण्याचे सूचित करते . पुढे एक विशिष्ट सल्ला येतो — आधी() — जी लक्ष्य पद्धत कॉल करण्यापूर्वी कार्यान्वित केली जाते. : greeting() हा कटपॉइंट आहे ज्याला हा सल्ला प्रतिसाद देतो. ठीक आहे, आणि खाली आपण पद्धतीचा मुख्य भाग पाहतो, जो जावा भाषेत लिहिलेला आहे, जो आपल्याला समजतो. जेव्हा आम्ही या पैलूसह मुख्य कार्य करतो तेव्हा आम्हाला हे कन्सोल आउटपुट मिळेल:
@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("ग्रीटिंग()") हा सल्ला आहे जो ग्रीटिंग() कटपॉइंटमध्ये नमूद केलेल्या कोडला कॉल करण्यापूर्वी लागू केला जातो .
उदाहरण क्रमांक २
समजा आमच्याकडे अशी काही पद्धत आहे जी क्लायंटसाठी काही ऑपरेशन्स करते आणि आम्ही या पद्धतीला मुख्य पासून म्हणतो :
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();
}
}
}
मुख्य मध्ये , व्हॅल्यू इन्स्टन्स व्हेरिएबलला व्हॅल्यू नियुक्त करण्यासाठी आम्ही सेटव्हॅल्यू वापरतो . मग व्हॅल्यू मिळवण्यासाठी आम्ही getValue वापरतो आणि नंतर ते 10 वर्णांपेक्षा मोठे आहे की नाही हे पाहण्यासाठी आम्ही checkValue कॉल करतो. तसे असल्यास, अपवाद फेकले जाईल. आता आपण पद्धतींचे कार्य लॉग करण्यासाठी कोणता पैलू वापरणार आहोत ते पाहू:
@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("execution(* *(..))") सर्व पद्धतींच्या सर्व कॉलमध्ये सामील होईल. @AfterReturning(value = "methodExecuting()", returning = "returningValue") हा सल्ला आहे जो लक्ष्य पद्धतीच्या यशस्वी अंमलबजावणीनंतर अंमलात आणला जाईल. आमच्याकडे येथे दोन प्रकरणे आहेत:
- जेव्हा पद्धतीमध्ये रिटर्न व्हॅल्यू असते — जर (returningValue! = Null) {
- जेव्हा कोणतेही रिटर्न व्हॅल्यू नसते — बाकी {
GO TO FULL VERSION