Spring AOP Example Tutorial – Aspect, Advice, Pointcut, JoinPoint, Annotations, XML Configuration

الإطار الربيعي يعتمد على مفهومين أساسيين – حقن الاعتمادات وبرمجة الجوانب الموجهة (Spring AOP).

برمجة الجوانب الموجهة في الربيع

لقد رأينا بالفعل كيفية عمل حقن الاعتمادات في الربيع، اليوم سنلقي نظرة على المفاهيم الأساسية لبرمجة الجوانب الموجهة وكيف يمكننا تنفيذها باستخدام الإطار الربيعي.

نظرة عامة على برمجة الجوانب الموجهة في الربيع

معظم تطبيقات المؤسسات لديها بعض الاهتمامات المشتركة التي تنطبق على أنواع مختلفة من الكائنات والوحدات. بعض الاهتمامات المشتركة تشمل تسجيل الأحداث، إدارة المعاملات، التحقق من صحة البيانات، إلخ. في البرمجة الموجهة للكائنات، يتم تحقيق تجزئة التطبيق بواسطة الفئات، بينما في برمجة المنظور الجانبي، يتم تحقيق تجزئة التطبيق بواسطة الجوانب ويتم تكوينها لقطع عبر فئات مختلفة. يزيل Spring AOP التبعية المباشرة للمهام المشتركة من الفئات التي لا يمكننا تحقيقها من خلال نموذج البرمجة الموجهة للكائنات العادي. على سبيل المثال، يمكننا أن نملك فئة منفصلة للتسجيل ولكن مرة أخرى يجب على الفئات الوظيفية أن تستدعي هذه الأساليب لتحقيق التسجيل عبر التطبيق.

مفاهيم جوهرية لبرمجة المنظور الجانبي

قبل أن نستكشف تنفيذ Spring AOP، يجب أن نفهم المفاهيم الأساسية لبرمجة المنظور الجانبي.

  1. الجانب (المنظور): الجانب هو فئة تنفذ اهتمامات تطبيق المؤسسة التي تمتد عبر عدة فئات، مثل إدارة المعاملات. يمكن أن تكون الجوانب فئة عادية مكونة من خلال تكوين XML في Spring أو يمكننا استخدام تكامل Spring AspectJ لتعريف فئة كجانب باستخدام تعليق @Aspect.
  2. نقطة الانضمام: نقطة الانضمام هي نقطة محددة في التطبيق مثل تنفيذ الطريقة، التعامل مع الاستثناءات، تغيير قيم متغيرات الكائن، إلخ. في AOP لـ Spring، تمثل نقطة الانضمام دائمًا تنفيذ الطريقة.
  3. النصيحة: النصائح هي الإجراءات التي يتم اتخاذها لنقطة انضمام معينة. من حيث البرمجة، هي الطرق التي يتم تنفيذها عندما يتم الوصول إلى نقطة انضمام معينة مع نقطة اقتطاع مطابقة في التطبيق. يمكنك التفكير في النصائح على أنها معرفات Struts2 أو مرشحات Servlet.

  4. نقطة القطع: نقطة القطع هي التعبيرات التي يتم مطابقتها مع نقاط الانضمام لتحديد ما إذا كانت النصيحة يجب تنفيذها أم لا. تستخدم نقطة القطع أنواعًا مختلفة من التعبيرات التي تتطابق مع نقاط الانضمام، ويستخدم إطار Spring للجوانب تعبير لغة نقطة القطع AspectJ.

  5. كائن الهدف: هي الكائنات التي يتم تطبيق النصائح عليها. تم تنفيذ AOP لـ Spring باستخدام الوكالات التي تعمل في الوقت الفعلي، لذا هذا الكائن دائمًا كائن محسن. ما يعنيه هو أنه يتم إنشاء فئة فرعية في وقت التشغيل حيث يتم تجاوز الطريقة الهدف وتضمين النصائح استنادًا إلى تكوينها.
  6. بروكسي AOP: تستخدم تنفيذات Spring AOP بروكسيات JDK الديناميكية لإنشاء فئات البروكسي مع فئات الهدف واستدعاءات النصائح، وتُعرف هذه باسم فئات بروكسي AOP. يمكننا أيضًا استخدام بروكسي CGLIB بإضافته كتبعية في مشروع Spring AOP.
  7. النسج: إنه عملية ربط الجوانب بكائنات أخرى لإنشاء كائنات البروكسي المستشيرة. يمكن القيام بذلك في وقت الترجمة، وقت التحميل، أو في وقت التشغيل. يقوم Spring AOP بأداء النسج في وقت التشغيل.

أنواع النصائح AOP

بناءً على استراتيجية تنفيذ النصيحة، تكون من الأنواع التالية.

  1. نصيحة Before: تعمل هذه النصائح قبل تنفيذ أساليب نقطة الانضمام. يمكننا استخدام تعليق @Before لتحديد نوع النصيحة كنصيحة Before.
  2. نصيحة After (أخيراً): نصيحة تُنفذ بعد انتهاء تنفيذ أسلوب نقطة الانضمام، سواء بشكل طبيعي أو عند رمي استثناء. يمكننا إنشاء نصيحة بعد باستخدام تعليق @After.
  3. نصيحة After Returning: في بعض الأحيان نريد أن تنفذ أساليب النصائح فقط إذا تم تنفيذ أسلوب نقطة الانضمام بشكل طبيعي. يمكننا استخدام تعليق @AfterReturning لتحديد الطريقة كنصيحة بعد الإرجاع.
  4. بعد إلقاء النصيحة: يتم تنفيذ هذه النصيحة فقط عندما يقوم أحد أساليب نقطة الانضمام برمي استثناء، يمكننا استخدامها لإلغاء التنفيذ بشكل تصريحي. نستخدم تعليق @AfterThrowing لهذا النوع من النصائح.
  5. نصيحة حول: هذه هي النصيحة الأكثر أهمية وقوة. تحيط هذه النصيحة بأسلوب نقطة الانضمام ويمكننا أيضًا اختيار ما إذا كان سننفذ أسلوب نقطة الانضمام أم لا. يمكننا كتابة كود النصيحة الذي يتم تنفيذه قبل وبعد تنفيذ أسلوب نقطة الانضمام. من مسؤولية النصيحة حول استدعاء أسلوب نقطة الانضمام وإرجاع القيم إذا كان الأسلوب يعيد شيئًا. نستخدم التعليق @Around لإنشاء أساليب النصيحة الحولية.

قد تبدو النقاط المذكورة أعلاه مربكة ولكن عندما ننظر إلى تنفيذ Spring AOP، ستكون الأمور أكثر وضوحًا. دعونا نبدأ في إنشاء مشروع Spring بسيط مع تنفيذات AOP. يوفر Spring دعمًا لاستخدام تعليمات AspectJ لإنشاء جوانب وسنستخدم ذلك للبساطة. تم تعريف جميع التعليقات AOP المذكورة أعلاه في حزمة org.aspectj.lang.annotation. أداة Spring Tool توفر معلومات مفيدة حول الجوانب، لذلك أنصحك باستخدامها. إذا كنت غير ملم بـ STS، فأوصيك بأن تلقي نظرة على برنامج تعليمي Spring MVC حيث شرحت كيفية استخدامه.

مثال Spring AOP

إنشاء مشروع جديد بنية بسيطة للربيع باستخدام Maven حتى تتم إضافة جميع مكتبات Spring Core في ملفات pom.xml ولا نحتاج إلى إدراجها بشكل صريح. سيبدو مشروعنا النهائي كما في الصورة أدناه. سننظر في تفاصيل مكونات الربيع الأساسية وتنفيذات الجانب الظاهر بشكل مفصل.

تبعيات Spring AOP AspectJ

توفر إطار الربيع دعمًا لـ AOP بشكل افتراضي ولكن نظرًا لأننا نستخدم تعليمات AspectJ لتكوين الجوانب والنصائح ، فإننا سنحتاج إلى إدراجها في ملف pom.xml.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>SpringAOPExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>4.0.2.RELEASE</spring-framework.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

		<!-- Test -->
		<junit.version>4.11</junit.version>

		<!-- AspectJ -->
		<aspectj.version>1.7.4</aspectj.version>

	</properties>

	<dependencies>
		<!-- Spring and Transactions -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>

		<!-- Logging with SLF4J & LogBack -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${slf4j.version}</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>${logback.version}</version>
			<scope>runtime</scope>
		</dependency>

		<!-- AspectJ dependencies -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${aspectj.version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjtools</artifactId>
			<version>${aspectj.version}</version>
		</dependency>
	</dependencies>
</project>

لاحظ أنني قمت بإضافة تبعيات aspectjrt و aspectjtools (الإصدار 1.7.4) في المشروع. كما قمت بتحديث إصدار إطار الربيع ليكون الإصدار الأحدث حتى الآن ، وهو 4.0.2.RELEASE.

فئة النموذج

لنقم بإنشاء فئة جافا بسيطة سنستخدمها في مثالنا مع بعض الطرق الإضافية. كود Employee.java:

package com.journaldev.spring.model;

import com.journaldev.spring.aspect.Loggable;

public class Employee {

	private String name;
	
	public String getName() {
		return name;
	}

	@Loggable
	public void setName(String nm) {
		this.name=nm;
	}
	
	public void throwException(){
		throw new RuntimeException("Dummy Exception");
	}	
}

هل لاحظت أن الطريقة setName() مُحَرَّرة بتعليق Loggable. إنها تعليق أنشأناه بناءً على Java المخصص المعرف من قِبَلنا في المشروع. سنبحث في استخدامها لاحقًا.

فئة الخدمة

لنقم بإنشاء فئة خدمة للعمل مع كائن الموظف. كود EmployeeService.java:

package com.journaldev.spring.service;

import com.journaldev.spring.model.Employee;

public class EmployeeService {

	private Employee employee;
	
	public Employee getEmployee(){
		return this.employee;
	}
	
	public void setEmployee(Employee e){
		this.employee=e;
	}
}

I could have used Spring annotations to configure it as a Spring Component, but we will use XML based configuration in this project. EmployeeService class is very standard and just provides us an access point for Employee beans.

تكوين Spring Bean مع AOP

إذا كنت تستخدم STS، لديك الخيار لإنشاء “ملف تكوين Spring Bean” واختيار مساحة أسماء AOP ولكن إذا كنت تستخدم بعض IDE آخر، يمكنك ببساطة إضافته في ملف تكوين spring bean. ملف تكوين مشروعي يبدو كما يلي. spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="https://www.springframework.org/schema/aop"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

<!-- Enable AspectJ style of Spring AOP -->
<aop:aspectj-autoproxy />

<!-- Configure Employee Bean and initialize it -->
<bean name="employee" class="com.journaldev.spring.model.Employee">
	<property name="name" value="Dummy Name"></property>
</bean>

<!-- Configure EmployeeService bean -->
<bean name="employeeService" class="com.journaldev.spring.service.EmployeeService">
	<property name="employee" ref="employee"></property>
</bean>

<!-- Configure Aspect Beans, without this Aspects advices wont execute -->
<bean name="employeeAspect" class="com.journaldev.spring.aspect.EmployeeAspect" />
<bean name="employeeAspectPointcut" class="com.journaldev.spring.aspect.EmployeeAspectPointcut" />
<bean name="employeeAspectJoinPoint" class="com.journaldev.spring.aspect.EmployeeAspectJoinPoint" />
<bean name="employeeAfterAspect" class="com.journaldev.spring.aspect.EmployeeAfterAspect" />
<bean name="employeeAroundAspect" class="com.journaldev.spring.aspect.EmployeeAroundAspect" />
<bean name="employeeAnnotationAspect" class="com.journaldev.spring.aspect.EmployeeAnnotationAspect" />

</beans>

لاستخدام AOP في Spring beans، نحتاج إلى القيام بالتالي:

  1. إعلان مساحة أسماء AOP مثل xmlns:aop=“https://www.springframework.org/schema/aop
  2. إضافة عنصر aop:aspectj-autoproxy لتمكين دعم Spring AspectJ مع وكيل تلقائي في الوقت التشغيلي
  3. تكوين فئات الجوانب كفئات Spring الأخرى

يمكنك رؤية أن لدي العديد من الجوانب المحددة في ملف تكوين فولفة الفولفة الربيعية، حان الوقت للنظر فيها واحدة تلو الأخرى.

مثال على جانب Spring AOP Before

كود EmployeeAspect.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAspect {

	@Before("execution(public String getName())")
	public void getNameAdvice(){
		System.out.println("Executing Advice on getName()");
	}
	
	@Before("execution(* com.journaldev.spring.service.*.get*())")
	public void getAllAdvice(){
		System.out.println("Service method getter called");
	}
}

النقاط المهمة في الفئة الجانبية أعلاه:

  • فئات الجوانب مطلوب أن تحتوي على تعليق @Aspect.
  • تستخدم تعليق @Before لإنشاء مشورة قبل.
    يتم استخدام التعليق @Before لإنشاء مشورة قبل.
  • السلسلة المعلمة المرسلة في تعليق @Before هي تعبير Pointcut.
  • ستقوم المشورة getNameAdvice() بالتنفيذ لأي طريقة Spring Bean بتوقيع public String getName(). هذه نقطة مهمة جدًا لتذكرها، إذا قمنا بإنشاء فولفة Employee باستخدام المشغل الجديد، فلن تُطبق المشورات. فقط عند استخدام ApplicationContext للحصول على الفولفة، ستُطبق المشورات.
  • يمكننا استخدام الرمز النجمي (*) كبطاقة ولوج في تعابير Pointcut، ستُطبق المشورة getAllAdvice() لجميع الفئات في الحزمة com.journaldev.spring.service التي تبدأ باسم get ولا تأخذ أي وسيطات.

سننظر في المشورة في العمل في فئة اختبار بعد أن ننظر إلى جميع أنواع المشورات المختلفة.

أساليب نقطة القطع في Spring AOP وإعادة الاستخدام

أحيانًا يجب علينا استخدام نفس تعبير نقطة القطع في أماكن متعددة، يمكننا إنشاء طريقة فارغة بتعليق @Pointcut ثم استخدامها كتعبير في النصائح. كود EmployeeAspectPointcut.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class EmployeeAspectPointcut {

	@Before("getNamePointcut()")
	public void loggingAdvice(){
		System.out.println("Executing loggingAdvice on getName()");
	}
	
	@Before("getNamePointcut()")
	public void secondAdvice(){
		System.out.println("Executing secondAdvice on getName()");
	}
	
	@Pointcut("execution(public String getName())")
	public void getNamePointcut(){}
	
	@Before("allMethodsPointcut()")
	public void allServiceMethodsAdvice(){
		System.out.println("Before executing service method");
	}
	
	// نقطة القطع للتنفيذ على جميع الطرق في الفئات في حزمة

	@Pointcut("within(com.journaldev.spring.service.*)")
	public void allMethodsPointcut(){}
	
}

المثال أعلاه واضح جدًا، بدلاً من التعبير نستخدم اسم الطريقة في وسم النصيحة.

Spring AOP نقطة الانضمام ووسائل النصيحة

يمكننا استخدام JoinPoint كمعلمة في طرق النصائح واستخدامها للحصول على توقيع الطريقة أو الكائن المستهدف. يمكننا استخدام التعبير args() في نقطة القطع التي سيتم تطبيقها على أي طريقة تطابق نمط الوسيطة. إذا استخدمنا هذا، فيجب علينا استخدام نفس الاسم في طريقة النصيحة من حيث يتم تحديد نوع الوسيطة. يمكننا أيضًا استخدام الكائنات العامة في وسائل النصيحة. كود EmployeeAspectJoinPoint.java:

package com.journaldev.spring.aspect;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAspectJoinPoint {
	
	@Before("execution(public void com.journaldev.spring.model..set*(*))")
	public void loggingAdvice(JoinPoint joinPoint){
		System.out.println("Before running loggingAdvice on method="+joinPoint.toString());
		
		System.out.println("Agruments Passed=" + Arrays.toString(joinPoint.getArgs()));

	}
	
	// وسائل النصيحة، سيتم تطبيقها على طرق الفول الذين لديهم وسيطة واحدة من نوع السلسلة
	@Before("args(name)")
	public void logStringArguments(String name){
		System.out.println("String argument passed="+name);
	}
}

مثال على نصائح Spring AOP بعد التنفيذ

لنلقي نظرة على فئة الجانب البسيطة مع مثال على النصائح بعد التنفيذ، بعد الإلقاء، وبعد العودة. كود EmployeeAfterAspect.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class EmployeeAfterAspect {

	@After("args(name)")
	public void logStringArguments(String name){
		System.out.println("Running After Advice. String argument passed="+name);
	}
	
	@AfterThrowing("within(com.journaldev.spring.model.Employee)")
	public void logExceptions(JoinPoint joinPoint){
		System.out.println("Exception thrown in Employee Method="+joinPoint.toString());
	}
	
	@AfterReturning(pointcut="execution(* getName())", returning="returnString")
	public void getNameReturningAdvice(String returnString){
		System.out.println("getNameReturningAdvice executed. Returned String="+returnString);
	}
	
}

يمكننا استخدام within في تعبير نقطة القطع لتطبيق النصيحة على جميع الطرق في الفئة. يمكننا استخدام نصيحة @AfterReturning للحصول على الكائن الذي تم إرجاعه بواسطة الطريقة المنصح بها. لدينا طريقة throwException() في الفئة Employee لعرض استخدام نصيحة After Throwing.

مثال على نصائح Spring AOP Around Aspect

كما شرح سابقًا، يمكننا استخدام جانب Around لقطع تنفيذ الطريقة قبلها وبعدها. يمكننا استخدامه للتحكم في ما إذا كانت الطريقة المنصح بها ستنفذ أم لا. يمكننا أيضًا فحص القيمة المُرجعة وتغييرها. هذه هي النصيحة الأقوى وتحتاج إلى تطبيق مناسب. كود EmployeeAroundAspect.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class EmployeeAroundAspect {

	@Around("execution(* com.journaldev.spring.model.Employee.getName())")
	public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
		System.out.println("Before invoking getName() method");
		Object value = null;
		try {
			value = proceedingJoinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("After invoking getName() method. Return value="+value);
		return value;
	}
}

حول النصائح دائمًا يُتوقع أن يكون لديها ProceedingJoinPoint كوسيط ويجب علينا استخدام أسلوب proceed() لاستدعاء أسلوب الكائن الهدف المستشير. إذا كان أسلوب الاستشارة يعيد شيئًا ما، فإنه من مسؤولية النصيحة إعادته إلى برنامج الcaller. بالنسبة لأساليب void، يمكن لأسلوب النصيحة إرجاع قيمة null. نظرًا لأن النصيحة around تقطع حول الأسلوب المستشير، يمكننا التحكم في إدخال وإخراج الأسلوب وسلوك تنفيذه.

نصائح Spring مع نقطة انطلاق مخصصة للتعليقات

إذا نظرت إلى جميع تعبيرات نقطة القطع المذكورة أعلاه، فهناك فرص لتطبيقها على بعض الفول الأخرى حيث لم يكن المقصود. على سبيل المثال، يمكن لشخص ما تحديد فول جديد في Spring بطريقة يحتوي على طريقة getName() وسيبدأ التوجيه في التطبيق على ذلك حتى لو لم يكن المقصود. لهذا السبب يجب علينا الحفاظ على نطاق تعبير نقطة القطع أضيق قدر الإمكان. النهج البديل هو إنشاء تعليق مخصص وتعليق الطرق التي نريد تطبيق التوجيه عليها. هذا هو الغرض من وجود طريقة setName() في Employee مع تعليق @Loggable. التعليق @Transactional في Spring Framework هو مثال رائع على هذا النهج لإدارة Spring Transaction. كود Loggable.java:

package com.journaldev.spring.aspect;

public @interface Loggable {

}

كود EmployeeAnnotationAspect.java:

package com.journaldev.spring.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class EmployeeAnnotationAspect {

	@Before("@annotation(com.journaldev.spring.aspect.Loggable)")
	public void myAdvice(){
		System.out.println("Executing myAdvice!!");
	}
}

سيتم تطبيق الطريقة myAdvice() فقط على طريقة setName(). هذا هو نهج آمن للغاية وكل ما نحتاج إليه عندما نريد تطبيق التوجيه على أي طريقة هو تعليقها بتعليق Loggable.

تكوين XML لـ Spring AOP

I always prefer annotation but we also have the option to configure aspects in the spring configuration file. For example, let’s say we have a class as below. EmployeeXMLConfigAspect.java code:

package com.journaldev.spring.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

public class EmployeeXMLConfigAspect {

	public Object employeeAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
		System.out.println("EmployeeXMLConfigAspect:: Before invoking getName() method");
		Object value = null;
		try {
			value = proceedingJoinPoint.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		System.out.println("EmployeeXMLConfigAspect:: After invoking getName() method. Return value="+value);
		return value;
	}
}

يمكننا تكوينها عن طريق تضمين التكوين التالي في ملف تكوين Spring Bean.

<bean name="employeeXMLConfigAspect" class="com.journaldev.spring.aspect.EmployeeXMLConfigAspect" />

<!-- Spring AOP XML Configuration -->
<aop:config>
	<aop:aspect ref="employeeXMLConfigAspect" id="employeeXMLConfigAspectID" order="1">
		<aop:pointcut expression="execution(* com.journaldev.spring.model.Employee.getName())" id="getNamePointcut"/>
		<aop:around method="employeeAroundAdvice" pointcut-ref="getNamePointcut" arg-names="proceedingJoinPoint"/>
	</aop:aspect>
</aop:config>

غرض عناصر تكوين AOP xml واضح من اسمها، لذا لن أدخل في التفاصيل كثيرا حولها.

مثال AOP لـ Spring

لنقم بكتابة برنامج بسيط باستخدام Spring ونرى كيف يؤثر كل هذه الجوانب على طرق الحبونة. كود SpringMain.java:

package com.journaldev.spring.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.service.EmployeeService;

public class SpringMain {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		EmployeeService employeeService = ctx.getBean("employeeService", EmployeeService.class);
		
		System.out.println(employeeService.getEmployee().getName());
		
		employeeService.getEmployee().setName("Pankaj");
		
		employeeService.getEmployee().throwException();
		
		ctx.close();
	}
}

الآن عند تنفيذ البرنامج أعلاه، نحصل على الناتج التالي.

Mar 20, 2014 8:50:09 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Thu Mar 20 20:50:09 PDT 2014]; root of context hierarchy
Mar 20, 2014 8:50:09 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Service method getter called
Before executing service method
EmployeeXMLConfigAspect:: Before invoking getName() method
Executing Advice on getName()
Executing loggingAdvice on getName()
Executing secondAdvice on getName()
Before invoking getName() method
After invoking getName() method. Return value=Dummy Name
getNameReturningAdvice executed. Returned String=Dummy Name
EmployeeXMLConfigAspect:: After invoking getName() method. Return value=Dummy Name
Dummy Name
Service method getter called
Before executing service method
String argument passed=Pankaj
Before running loggingAdvice on method=execution(void com.journaldev.spring.model.Employee.setName(String))
Agruments Passed=[Pankaj]
Executing myAdvice!!
Running After Advice. String argument passed=Pankaj
Service method getter called
Before executing service method
Exception thrown in Employee Method=execution(void com.journaldev.spring.model.Employee.throwException())
Exception in thread "main" java.lang.RuntimeException: Dummy Exception
	at com.journaldev.spring.model.Employee.throwException(Employee.java:19)
	at com.journaldev.spring.model.Employee$$FastClassBySpringCGLIB$$da2dc051.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:711)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
	at com.journaldev.spring.model.Employee$$EnhancerBySpringCGLIB$$3f881964.throwException(<generated>)
	at com.journaldev.spring.main.SpringMain.main(SpringMain.java:17)

يمكنك أن ترى أن النصائح تنفذ واحدة تلو الأخرى استنادًا إلى تكوينات نقاط القطع. يجب عليك تكوينها واحدة تلو الأخرى لتجنب الارتباك. هذا كل شيء بالنسبة لـ دليل مثال AOP لـ Spring، آمل أن تكون قد تعلمت أساسيات AOP مع Spring وتستطيع التعلم المزيد من الأمثلة. قم بتنزيل مشروع العينة من الرابط أدناه واستمتع بتجربته.

تنزيل مشروع Spring AOP

Source:
https://www.digitalocean.com/community/tutorials/spring-aop-example-tutorial-aspect-advice-pointcut-joinpoint-annotations