דוגמה ל־Spring AOP – Aspect, Advice, Pointcut, JoinPoint, Annotations, תצורה ב־XML

מסגרת הקוד הפניאלית מופתחת על פי שני עקרונות יסוד – התלות בהזרקה ותכנות מונה כיוונים (Spring AOP).

Spring AOP

כבר ראינו איך התלות בהזרקה של Spring עובדת, היום נסתכל על עקרונות התכנות המונה כיוונים וכיצד אנו יכולים ליישם אותם באמצעות מסגרת הקוד הפניאלית.

סקירת Spring AOP

רוב היישומים העסקיים מכילים מספר דאגות צד-צד שנפוצות לסוגים שונים של אובייקטים ומודולים. כמה מדאגות הצד-צד הנפוצות הן הלוגינג, ניהול העסקאות, אימות הנתונים ועוד. בתכנות מונחה עצמים, המודולריות של היישום מושגת על ידי המחלקות, בעוד בתכנות מונחה צד-צד, המודולריות של היישום מושגת על ידי צדות והן מוגדרות כדי לחתוך דרך רחבה יותר של שימוש במחלקות שונות. Spring AOP מסיר את תלות הישירה של המשימות הצד-צד מהמחלקות אותן איננו יכולים להשיג דרך הדגם המקובל של תכנות מונחה עצמים. לדוגמה, אנו יכולים להשתמש במחלקה נפרדת ללוגינג, אך שוב, על המחלקות הפונקציונליות לקרוא לשיטות אלו כדי להשיג לוגינג בכל היישום.

מושגים מרכזיים בתכנות מונחה צד-צד

לפני שנתעמק במימוש של Spring AOP, עלינו להבין את המושגים המרכזיים של תכנות מונחה צד-צד.

  1. צד: צד הוא מחלקה שמיישמת דאגות יישום עסקיות שחותכות על מספר מחלקות, כגון ניהול העסקאות. צדים יכולים להיות מחלקה רגילה שמוגדרת דרך תצורת XML של Spring או שניתן להשתמש באינטגרציה של Spring AspectJ כדי להגדיר מחלקה כצד באמצעות האנוטציה @Aspect.
  2. נקודת הצטרפות: נקודת הצטרפות היא נקודה ספציפית ביישום כגון ביצוע של שיטה, טיפול בשגיאות, שינוי ערכי משתנה אובייקט וכו'. ב- Spring AOP, נקודת הצטרפות היא תמיד ביצוע של שיטה.
  3. ייעוץ: ייעוצים הם פעולות שנעשות עבור נקודת צטרפות מסוימת. מבחינה של תכנות, אלו הם שיטות שמבוצעות כאשר נקודת הצטרפות מסוימת עם פיינטקאט תואם מושגת ביישום. ניתן לחשוב על ייעוצים כמו מתפסי Struts2 או מסנני Servlet.
  4. פוינטקאט: פוינטקאט הם ביטויים שמתאימים לנקודות הצטרפות כדי לקבוע האם ייעוץ יצטרך להתבצע או לא. פוינטקאט משתמש בסוגים שונים של ביטויים שמתאימים לנקודות הצטרפות ומסגרת ה- Spring משתמשת בשפת ביטויי הפוינטקאט של AspectJ.
  5. אובייקט יעד: אלה הם האובייקטים שבהם מיושמים ייעוצים. AOP של Spring מיושם באמצעות פרוקסיות בזמן ריצה, לכן האובייקט הזה הוא תמיד אובייקט מסוג פרוקסי. מה שזה אומר הוא שנוצרת תת-מחלקה בזמן ריצה שבה מתבצעת שיטת היעד והיעוצים מתווספים על פי התצורה שלהם.
  6. פרוקסי AOP: מימוש AOP של Spring משתמש בפרוקסי דינמי של JDK כדי ליצור את הקבוצות הפרוקסיות עם קבוצות היעד ושיחות הייעוץ, נקראות גם כך קבוצות הפרוקסי AOP. אנחנו יכולים גם להשתמש בפרוקסי CGLIB על ידי הוספתו כתלות בפרויקט ה-AOP של Spring.
  7. עיבוד: זהו התהליך של קישור האספקטים לאובייקטים אחרים כדי ליצור את האובייקטים הפרוקסיים שנעצרו. ניתן לבצע את העיבוד בזמן ההידור, בזמן הטעינה או בזמן הריצה. Spring AOP מבצע עיבוד בזמן הריצה.

סוגי ייעוץ AOP

בהתאם לאסטרטגיה של ביצוע הייעוץ, הם מתחלקים לסוגים הבאים.

  1. ייעוץ לפני: אלו ייעוצים מופעלים לפני ביצוע של פעולות נקודות ההצטרפות. אנחנו יכולים להשתמש בהערת @Before כדי לסמן סוג ייעוץ כייעוץ לפני.
  2. ייעוץ אחרי (בסופו של דבר): ייעוץ שמופעל לאחר שסיים להתבצע פעולה בנקודת ההצטרפות, באופן תקני או על ידי השלכת חריגה. אנחנו יכולים ליצור ייעוץ אחרי באמצעות הערת @After.
  3. ייעוץ לאחר חזרה: לפעמים אנחנו רוצים שיטות הייעוץ יופעלו רק אם הפעולה בנקודת ההצטרפות מתבצעת באופן תקני. אנחנו יכולים להשתמש בהערת @AfterReturning כדי לסמן שיטה כייעוץ לאחר חזרה.
  4. לאחר ייעוץ מזרים: יישום זה מתבצע רק כאשר פונקציית נקודת הציר מזרימה שגיאה, ניתן להשתמש בו על מנת לבצע ביטול עסקה באופן דקלרטיבי. אנחנו משתמשים בהערה @AfterThrowing עבור סוג זה של ייעוץ.
  5. ייעוץ מסביב: יישום זה הוא הייעוץ החשוב והעוצמתי ביותר. ייעוץ זה מקיף את פונקציית נקודת הציר ואנחנו יכולים גם לבחור האם לבצע את פונקציית נקודת הציר או לא. אנחנו יכולים לכתוב קוד ייעוץ שמתבצע לפני ואחרי ביצוע פונקציית נקודת הציר. זהו אחריות האספקט לקרוא לפונקציית נקודת הציר ולהחזיר ערכים אם הפונקציה מחזירה משהו. אנחנו משתמשים בהערה @Around כדי ליצור ייעוץ מסביב.

הנקודות שצוינו לעיל עשויות להיות מבלבלות, אך כאשר נביט במימוש של Spring AOP, הדברים יהיו יותר ברורים. בואו נתחיל ביצירת פרויקט Spring פשוט עם מימושי AOP. Spring מספק תמיכה בשימוש של הערות AspectJ כדי ליצור אספקטים ואנחנו נשתמש בזה בגלל הפשטות. כל ההערות AOP המצוינות לעיל מוגדרות בחבילת org.aspectj.lang.annotation. Spring Tool Suite מספקת מידע שימושי על האספקטים, לכן אני ממליץ להשתמש בו. אם אתה לא מכיר את STS, אני ממליץ לך להציץ ב-מדריך Spring MVC שבו הסברתי כיצד להשתמש בו.

דוגמת Spring AOP

צור פרויקט חדש של Spring Maven פשוט, כך שכל ספריות ה־Spring Core יכללו בקובץ ה־pom.xml ולא נצטרך לכלול אותן באופן מפורש. הפרויקט הסופי שלנו יראה כמו התמונה למטה, נבחן באופן מפורט את רכיבי ה־Spring Core ואת מימושי ה־Aspect.

תלות AOP AspectJ של Spring

ה־Spring framework מספק תמיכה ב־AOP כבר כתקן אבל מאחר ואנחנו משתמשים ב־AspectJ annotations לתצורת רכיבים וייעוץ, יהיה עלינו לכלול אותם בקובץ ה־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) בפרויקט. גם עדכנתי את גרסת ה־Spring framework להיות הגרסה האחרונה עד כה, כלומר 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, אבל אם אתה משתמש בסביבת פיתוח אחרת, תוכל פשוט להוסיף את זה לקובץ התצורת של פעולות ה-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>

כדי להשתמש ב-Spring AOP ב-Bean של Spring, עלינו לבצע את השלבים הבאים:

  1. הכרז על מרחב השם AOP כמו xmlns:aop="https://www.springframework.org/schema/aop"
  2. הוסף אלמנט aop:aspectj-autoproxy כדי לאפשר תמיכה ב-Spring AspectJ עם אוטומטיית פרוקסי בזמן ריצה
  3. הגדר כתבות Aspect כ-Bean של Spring כמו שמתבצע עם שאר פעולות Spring

אתה יכול לראות שיש לי הרבה נושאים שהוגדרו בקובץ תצורת הפונים של Spring, זה הזמן לבדוק אותם אחד אחד.

דוגמה לאספקט Spring AOP לפני

קוד 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 חייבות להכיל את האנוטציה @Aspect.
  • האנוטציה @Before משמשת ליצירת ייעוץ לפני
  • הפרמטר המחרוזת שמועברת באנוטציה @Before היא ביטוי ה- Pointcut
  • הייעוץ getNameAdvice() יופעל עבור כל שיטת Spring Bean עם חתימה public String getName(). נקודה חשובה מאוד לזכור, אם ניצור את אובייקט המודל משתמשים באופרטור new, הייעוצים לא יימצאו. רק כאשר נשתמש ב- 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()));

	}
	
	//ארגומנטי הייעוץ, ייחולו על שיטות האובייקט עם ארגומנט יחיד מסוג String
	@Before("args(name)")
	public void logStringArguments(String name){
		System.out.println("String argument passed="+name);
	}
}

דוגמה לאספקט After של 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 בביטוי ה־pointcut כדי ליישם את האספקט על כל השיטות במחלקה. אנחנו יכולים להשתמש בחיבור @AfterReturning כדי לקבל את האובייקט שמתוארך על ידי השיטה שקובעה. יש לנו שיטה של throwException() בעץ ה־Employee שמציגה את השימוש של טיפול אחרי זריקה.

דוגמה לאספקט Around של Spring AOP

כפי שנסביר למעלה, אנחנו יכולים להשתמש באספקט 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() שלו כדי לקרוא לשיטת המטרה של האובייקט המועצה. אם השיטה המועצה מחזירה משהו, זה על אחריות הייעוץ להחזיר את זה לתוכנית הקוראת. לשיטות void, השיטה המועצה יכולה להחזיר null. מאחר והייעוץ מחתיך סביב לשיטת המועצה, אנו יכולים לשלוט בקלט ובפלט של השיטה וכן בהתנהגות הביצוע שלה.

ייעוץ Spring עם Pointcut מותאם אישית

אם תביט בכל ייעוץ הביטויי נקודה מעלה שנכתב לעיל, יש סיכוי שהם ייחלפו בבניינים נוספים שבהם זה לא נדרש. לדוגמה, מישהו יכול להגדיר בון ספרינג חדש עם שיטת getName() והייעוץ יתחיל להתיישב על זה גם אם זה לא נדרש. זו הסיבה לכך שעלינו לשמור על טווח ביטויי נקודה מעלה כמה שיותר צר. גישה חלופית היא ליצור הערה מותאמת אישית ולסמן את השיטות שברצוננו להחיל עליהן העצה. זהו מטרת הוספת הערה setName() לשיטת Employee איתה @Loggable מסומנת. הערה @Transactional במסגרת Spring Framework היא דוגמה נהדרת לגישה זו עבור ניהול טרנזקציות של Spring. קוד 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.

תצורת Spring AOP XML

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 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 ותוכל ללמוד עוד מדוגמאות. ניתן להוריד את הפרויקט הדוגמה מהקישור למטה ולשחק עם זה.

הורד את פרויקט ה-AOP של Spring

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