AOP کا اطلاق کرنا
پہلو پر مبنی پروگرامنگ کو کراس کٹنگ کے کاموں کو انجام دینے کے لیے ڈیزائن کیا گیا ہے، جو کوئی بھی کوڈ ہو سکتا ہے جسے مختلف طریقوں سے کئی بار دہرایا جا سکتا ہے، جسے مکمل طور پر علیحدہ ماڈیول میں نہیں بنایا جا سکتا۔ اس کے مطابق، AOP ہمیں اسے مرکزی کوڈ سے باہر رکھنے اور اسے عمودی طور پر اعلان کرنے دیتا ہے۔ ایک مثال ایپلی کیشن میں سیکیورٹی پالیسی کا استعمال کرنا ہے۔ عام طور پر، سیکیورٹی ایپلی کیشن کے بہت سے عناصر کے ذریعے چلتی ہے۔ مزید یہ کہ ایپلی کیشن کی سیکیورٹی پالیسی کو ایپلی کیشن کے تمام موجودہ اور نئے حصوں پر یکساں طور پر لاگو کیا جانا چاہیے۔ ایک ہی وقت میں، استعمال میں سیکیورٹی پالیسی خود تیار ہوسکتی ہے۔ یہ AOP استعمال کرنے کے لیے بہترین جگہ ہے ۔ اس کے علاوہ، ایک اور مثال لاگنگ ہے ۔ لاگنگ فنکشنل کو دستی طور پر شامل کرنے کے بجائے لاگنگ کے لیے AOP اپروچ استعمال کرنے کے کئی فوائد ہیں:-
لاگنگ کے لیے کوڈ کو شامل کرنا اور ہٹانا آسان ہے: آپ کو بس کچھ پہلوؤں کے کنفیگریشنز کو شامل یا ہٹانا ہے۔
-
لاگنگ کے لیے تمام سورس کوڈ ایک جگہ پر رکھے گئے ہیں، لہذا آپ کو دستی طور پر ان تمام جگہوں کو تلاش کرنے کی ضرورت نہیں ہے جہاں اسے استعمال کیا گیا ہے۔
-
لاگنگ کوڈ کہیں بھی شامل کیا جا سکتا ہے، چاہے ان طریقوں اور کلاسوں میں جو پہلے سے لکھے جا چکے ہوں یا نئی فعالیت میں۔ یہ کوڈنگ کی غلطیوں کی تعداد کو کم کرتا ہے۔
اس کے علاوہ، ڈیزائن کنفیگریشن سے کسی پہلو کو ہٹاتے وقت، آپ اس بات کا یقین کر سکتے ہیں کہ تمام ٹریسنگ کوڈ ختم ہو گئے ہیں اور کچھ بھی نہیں چھوڑا گیا۔
- پہلو الگ الگ کوڈ ہیں جنہیں بہتر بنایا جا سکتا ہے اور بار بار استعمال کیا جا سکتا ہے۔
AOP کے بنیادی اصول
اس موضوع میں مزید آگے بڑھنے کے لیے، آئیے پہلے AOP کے بنیادی تصورات کو جانیں۔ مشورہ - اضافی منطق یا کوڈ جوائن پوائنٹ سے بلایا جاتا ہے۔ جوائن پوائنٹ سے پہلے، بعد میں یا اس کے بجائے مشورہ دیا جا سکتا ہے (ذیل میں ان کے بارے میں مزید)۔ مشورے کی ممکنہ اقسام :-
اس سے پہلے - اس قسم کے مشورے کو ہدف کے طریقوں، یعنی جوائن پوائنٹس پر عمل درآمد سے پہلے شروع کیا جاتا ہے۔ پہلوؤں کو کلاسز کے طور پر استعمال کرتے وقت، ہم @Before تشریح کا استعمال کرتے ہیں تاکہ پہلے آنے والے مشورے کو نشان زد کریں۔ پہلوؤں کو .aj فائلوں کے طور پر استعمال کرتے وقت ، یہ پہلے () طریقہ ہوگا ۔
- اس کے بعد - طریقوں (جوائن پوائنٹس) پر عمل درآمد کے بعد جو مشورہ دیا جاتا ہے وہ مکمل ہو جاتا ہے، عام عمل درآمد کے ساتھ ساتھ استثناء پھینکتے وقت بھی۔
پہلوؤں کو بطور کلاس استعمال کرتے وقت، ہم @After تشریح کا استعمال اس بات کی نشاندہی کرنے کے لیے کر سکتے ہیں کہ یہ مشورہ ہے جو بعد میں آتا ہے۔
جب پہلوؤں کو .aj فائلوں کے طور پر استعمال کرتے ہیں، تو یہ after() طریقہ ہے۔
-
واپسی کے بعد - یہ مشورہ صرف اس وقت انجام دیا جاتا ہے جب ہدف کا طریقہ عام طور پر بغیر کسی غلطی کے ختم ہو جائے۔
جب پہلوؤں کو کلاسز کے طور پر پیش کیا جاتا ہے، تو ہم @AfterReturning تشریح کا استعمال کر سکتے ہیں تاکہ مشورے کو کامیاب تکمیل کے بعد عمل میں لایا جا سکے۔
پہلوؤں کو .aj فائلوں کے طور پر استعمال کرتے وقت، یہ after() واپسی (Object obj) کا طریقہ ہوگا ۔
-
پھینکنے کے بعد - یہ مشورہ ان مثالوں کے لیے ہے جب کوئی طریقہ، یعنی جوائن پوائنٹ، ایک استثناء پھینکتا ہے۔ ہم اس مشورے کو کچھ قسم کے ناکام عمل سے نمٹنے کے لیے استعمال کر سکتے ہیں (مثال کے طور پر، پورے لین دین کو واپس کرنے کے لیے یا مطلوبہ ٹریس لیول کے ساتھ لاگ ان کرنے کے لیے)۔
طبقاتی پہلوؤں کے لیے، @AfterThrowing تشریح کا استعمال اس بات کی نشاندہی کرنے کے لیے کیا جاتا ہے کہ یہ مشورہ مستثنیٰ پھینکنے کے بعد استعمال کیا جاتا ہے۔
پہلوؤں کو .aj فائلوں کے طور پر استعمال کرتے وقت، یہ after() پھینکنے کا (Exception e) طریقہ ہوگا ۔
-
ارد گرد - شاید مشورہ کی سب سے اہم اقسام میں سے ایک۔ یہ ایک طریقہ کے ارد گرد ہے، یعنی، ایک جوائن پوائنٹ جسے ہم استعمال کر سکتے ہیں، مثال کے طور پر، منتخب کریں کہ آیا جوائن پوائنٹ کا دیا ہوا طریقہ انجام دیا جائے یا نہیں۔
آپ ایڈوائس کوڈ لکھ سکتے ہیں جو جوائن پوائنٹ کے طریقہ کار پر عمل درآمد سے پہلے اور بعد میں چلتا ہے۔
ارد گرد کا مشورہ جوائن پوائنٹ کے طریقہ کار کو کال کرنے کے لیے ذمہ دار ہے اور اگر طریقہ کچھ لوٹاتا ہے تو واپسی کی اقدار۔ دوسرے لفظوں میں، اس مشورے میں، آپ بغیر کسی ٹارگٹ میتھڈ کے آپریشن کو بغیر کال کیے نقل کر سکتے ہیں، اور واپسی کے نتیجے میں آپ جو چاہیں واپس کر سکتے ہیں۔
کلاسز کے طور پر پہلوؤں کو دیکھتے ہوئے، ہم @Around تشریح کا استعمال کرتے ہوئے مشورہ تخلیق کرتے ہیں جو جوائن پوائنٹ کو لپیٹتا ہے۔ .aj فائلوں کی شکل میں پہلوؤں کو استعمال کرتے وقت ، یہ طریقہ ارد گرد() طریقہ ہوگا ۔
-
کمپائل ٹائم ویونگ — اگر آپ کے پاس اسپیکٹ کا سورس کوڈ اور وہ کوڈ ہے جہاں آپ اسپیکٹ کو استعمال کرتے ہیں، تو آپ اسپیکٹ جے کمپائلر کا استعمال کرتے ہوئے سورس کوڈ اور پہلو کو براہ راست مرتب کر سکتے ہیں۔
-
پوسٹ کمپائل ویونگ (بائنری ویونگ) — اگر آپ کوڈ میں پہلوؤں کو بُننے کے لیے سورس کوڈ کی تبدیلیوں کو استعمال نہیں کر سکتے یا نہیں کرنا چاہتے، تو آپ پہلے سے مرتب شدہ کلاسز یا جار فائلیں لے سکتے ہیں اور ان میں پہلوؤں کو انجیکشن لگا سکتے ہیں۔
-
لوڈ ٹائم ویونگ - یہ صرف بائنری ویونگ ہے جس میں اس وقت تک تاخیر ہوتی ہے جب تک کہ کلاس لوڈر کلاس فائل کو لوڈ نہیں کرتا اور JVM کے لیے کلاس کی وضاحت نہیں کرتا۔
اس کو سپورٹ کرنے کے لیے ایک یا زیادہ ویونگ کلاس لوڈرز کی ضرورت ہے۔ وہ یا تو واضح طور پر رن ٹائم کے ذریعے فراہم کیے جاتے ہیں یا "ویونگ ایجنٹ" کے ذریعے فعال کیے جاتے ہیں۔
جاوا میں مثالیں۔
اگلا، AOP کی بہتر تفہیم کے لیے ، ہم "Hello World" طرز کی چھوٹی مثالیں دیکھیں گے۔ بلے کے دائیں طرف، میں نوٹ کروں گا کہ ہماری مثالیں compile-time weaving استعمال کریں گی ۔ سب سے پہلے، ہمیں اپنی 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>
اس کے بعد، Maven
سے دوبارہ درآمد کرنا اور mvn clean compile کو چلانے کا ایک اچھا خیال ہے ۔ اب آئیے براہ راست مثالوں کی طرف چلتے ہیں۔
مثال نمبر 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, ");
}
}
یہ فائل کلاس کی طرح ہے۔ آئیے دیکھتے ہیں کہ یہاں کیا ہو رہا ہے: پوائنٹ کٹ جوائن پوائنٹس کا ایک سیٹ ہے۔ greeting() اس پوائنٹ کٹ کا نام ہے۔ : ایگزیکیوشن Main.printName(...) طریقہ کی تمام ( * ) کالوں پر عمل درآمد کے دوران اسے لاگو کرنے کا اشارہ کرتا ہے۔ اس کے بعد ایک مخصوص مشورہ آتا ہے — before() — جو ہدف کے طریقہ کار کو بلانے سے پہلے عمل میں لایا جاتا ہے۔ : 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("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 آبجیکٹ کے proceed طریقہ کے ساتھ ، ہم مشورے میں اس کے مقام کا تعین کرنے کے لیے ریپنگ کا طریقہ کہتے ہیں۔ لہذا، اوپر کے طریقہ کار میں کوڈ joinPoint.proceed(); اس سے پہلے ہے ، جبکہ اس کے نیچے کا کوڈ After ہے ۔ اگر ہم چلاتے ہیں main ، تو ہمیں یہ کنسول میں ملتا ہے:
public static void performSomeOperation(String clientName) throws Exception {
System.out.println("Performing some operations for Client " + clientName);
throw new Exception();
}
پھر ہمیں یہ کنسول آؤٹ پٹ ملتا ہے:
مثال نمبر 3
ہماری اگلی مثال میں، آئیے کچھ ایسا کرتے ہیں جیسے کنسول میں لاگ ان کرنا۔ سب سے پہلے، Main پر ایک نظر ڈالیں ، جہاں ہم نے کچھ سیڈو کاروباری منطق شامل کی ہے: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 کا استعمال کرتے ہیں، اور پھر ہم یہ دیکھنے کے لیے 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("execution(* *(..))") تمام طریقوں کی تمام کالوں میں شامل ہو جائے گا۔ @AfterReturning(value = "methodExecuting()", returning = "returningValue") وہ مشورہ ہے جو ہدف کے طریقہ کار کی کامیابی کے بعد عمل میں لایا جائے گا۔ ہمارے یہاں دو صورتیں ہیں:
- جب طریقہ کی واپسی کی قدر ہوتی ہے — if (returningValue! = Null) {
- جب واپسی کی کوئی قیمت نہ ہو — ورنہ {
GO TO FULL VERSION