Учебное пособие по Spring AOP — Aspect, Advice, Pointcut, JoinPoint, Annotations, XML Configuration
Spring Framework разработан на основе двух основных концепций — внедрения зависимостей и аспектно-ориентированного программирования (Spring AOP).
Весенний АОП
Мы уже видели, как работает Spring Dependency Injection, сегодня мы рассмотрим основные концепции аспектно-ориентированного программирования и то, как мы можем реализовать его с помощью Spring Framework.
Обзор Spring АОП
Большинство корпоративных приложений имеют некоторые общие сквозные проблемы, применимые к различным типам объектов и модулей. Некоторыми из общих сквозных проблем являются ведение журнала, управление транзакциями, проверка данных и т. Д. В объектно-ориентированном программировании модульность приложения достигается с помощью классов, тогда как в аспектно-ориентированном программировании модульность приложения достигается с помощью аспектов, и они настроены для пересечения различных классов. Spring AOP устраняет прямую зависимость сквозных задач от классов, чего мы не можем достичь с помощью обычной модели объектно-ориентированного программирования. Например, у нас может быть отдельный класс для ведения журнала, но опять же функциональные классы должны будут вызывать эти методы для обеспечения ведения журнала во всем приложении.
Ключевые понятия аспектно-ориентированного программирования
Прежде чем мы углубимся в реализацию Spring AOP, мы должны понять основные концепции AOP.
- Аспект. Аспект – это класс, реализующий функции корпоративного приложения, охватывающие несколько классов, например управление транзакциями. Аспекты могут быть обычным классом, настроенным с помощью конфигурации XML Spring, или мы можем использовать интеграцию Spring AspectJ, чтобы определить класс как Aspect с помощью аннотации
@Aspect
. - Точка присоединения. Точка присоединения – это конкретная точка в приложении, например, выполнение метода, обработка исключений, изменение значений объектных переменных и т. д. В Spring AOP точка присоединения – это всегда выполнение метода.
- Совет. Советы – это действия, выполняемые для определенной точки соединения. С точки зрения программирования, это методы, которые выполняются, когда в приложении достигается определенная точка соединения с соответствующим pointcut. Советы можно рассматривать как фильтры сервлетов.
- Pointcut: Pointcut — это выражения, которые сопоставляются с точками соединения, чтобы определить, нужно ли выполнять совет или нет. Pointcut использует различные виды выражений, которые сопоставляются с точками соединения, а платформа Spring использует язык выражений pointcut AspectJ.
- Целевой объект: это объект, к которому применяются рекомендации. Spring AOP реализован с использованием прокси-серверов времени выполнения, поэтому этот объект всегда является прокси-объектом. Это означает, что во время выполнения создается подкласс, в котором целевой метод переопределяется, а рекомендации включаются в зависимости от их конфигурации.
- Прокси-сервер AOP. В реализации Spring AOP используется динамический прокси-сервер JDK для создания классов прокси с целевыми классами и вызовами рекомендаций, которые называются классами прокси-серверов AOP. Мы также можем использовать прокси-сервер CGLIB, добавив его в качестве зависимости в проект Spring AOP.
- Сплетение: это процесс связывания аспектов с другими объектами для создания рекомендованных прокси-объектов. Это можно сделать во время компиляции, во время загрузки или во время выполнения. Spring AOP выполняет переплетение во время выполнения.
Типы рекомендаций АОП
В зависимости от стратегии исполнения советов они бывают следующих видов.
- Перед советом. Эти советы запускаются перед выполнением методов точки соединения. Мы можем использовать аннотацию
@Before
, чтобы пометить тип совета как «до совета». - После (наконец) совета: совет, который выполняется после завершения выполнения метода точки соединения, либо в обычном режиме, либо путем создания исключения. Мы можем создать совет после, используя аннотацию
@After
. - После возврата совета. Иногда нам нужно, чтобы методы совета выполнялись только в том случае, если метод точки соединения выполняется нормально. Мы можем использовать аннотацию
@AfterReturning
, чтобы пометить метод как после возврата совета. - After Throwing Advice: этот совет выполняется только тогда, когда метод точки соединения генерирует исключение, мы можем использовать его для декларативного отката транзакции. Мы используем аннотацию
@AfterThrowing
для советов такого типа. - Around Advice: это самый важный и действенный совет. Этот совет касается метода точки соединения, и мы также можем выбрать, выполнять ли метод точки соединения или нет. Мы можем написать код совета, который будет выполняться до и после выполнения метода точки соединения. Совет вокруг должен вызывать метод точки соединения и возвращать значения, если метод что-то возвращает. Мы используем аннотацию
@Around
для создания методов консультации вокруг.
Упомянутые выше моменты могут показаться запутанными, но когда мы рассмотрим реализацию Spring AOP, все станет более ясно. Давайте начнем создавать простой проект Spring с реализациями АОП. Spring поддерживает использование аннотаций AspectJ для создания аспектов, и мы будем использовать это для простоты. Все вышеуказанные аннотации АОП определены в пакете org.aspectj.lang.annotation
. Spring Tool Suite предоставляет полезную информацию об аспектах, поэтому я бы посоветовал вам использовать ее. Если вы не знакомы с STS, я бы порекомендовал вам взглянуть на Spring MVC Tutorial, где я объяснил, как его использовать.
Весенний пример АОП
Зависимости Spring AOP AspectJ
Платформа Spring обеспечивает поддержку 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). Также я обновил версию среды Spring, чтобы она была последней на сегодняшний день, то есть 4.0.2.RELEASE.
Класс модели
Давайте создадим простой Java-бин, который мы будем использовать в нашем примере с некоторыми дополнительными методами. Код сотрудника.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, определенная нами в проекте. Мы рассмотрим его использование позже.
Класс обслуживания
Давайте создадим сервисный класс для работы с bean-компонентом Employee. Код 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;
}
}
Я мог бы использовать аннотации Spring, чтобы настроить его как компонент Spring, но в этом проекте мы будем использовать конфигурацию на основе XML. Класс EmployeeService очень стандартный и просто предоставляет нам точку доступа для бинов Employee.
Конфигурация Spring Bean с AOP
Если вы используете STS, у вас есть возможность создать «Файл конфигурации Spring Bean» и выбрать пространство имен схемы AOP, но если вы используете какую-либо другую IDE, вы можете просто добавить его в файл конфигурации Spring Bean. Файл конфигурации моего проекта bean выглядит так, как показано ниже.
<?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 в компонентах Spring нам необходимо сделать следующее:
- Объявите пространство имен AOP, например xmlns:aop=\https://www.springframework.org/schema/aop
- Добавьте элемент aop:aspectj-autoproxy, чтобы включить поддержку Spring AspectJ с автоматическим прокси во время выполнения.
- Настроить классы Aspect как другие компоненты Spring
Вы можете видеть, что у меня есть много аспектов, определенных в файле конфигурации bean-компонента 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
. - Аннотация @Before используется для создания рекомендации «До»
- Строковый параметр, передаваемый в аннотации
@Before
, представляет собой выражение Pointcut Совет - getNameAdvice() будет выполняться для любого метода Spring Bean с сигнатурой
public String getName()
. Это очень важный момент, который следует помнить: если мы создадим компонент Employee с помощью нового оператора, советы не будут применяться. Советы будут применяться только тогда, когда мы будем использовать ApplicationContext для получения компонента. - Можно использовать звездочку (*) в качестве подстановочного знака в выражениях Pointcut, getAllAdvice() будет применяться ко всем классам в пакете
com.journaldev.spring.service
имя которого начинается сget
и не принимает никаких аргументов.
Мы рассмотрим советы в действии на тестовом занятии после того, как изучим все различные типы советов.
Методы Spring AOP Pointcut и повторное использование
Иногда нам приходится использовать одно и то же выражение Pointcut в нескольких местах, мы можем создать пустой метод с аннотацией @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 to execute on all the methods of classes in a package
@Pointcut("within(com.journaldev.spring.service.*)")
public void allMethodsPointcut(){}
}
Приведенный выше пример очень ясен, вместо выражения мы используем имя метода в аргументе аннотации совета.
Spring AOP JoinPoint и аргументы Advice
Мы можем использовать JoinPoint в качестве параметра в методах-советах и с его помощью получить сигнатуру метода или целевой объект. Мы можем использовать выражение args()
в pointcut для применения к любому методу, который соответствует шаблону аргумента. Если мы используем это, то нам нужно использовать то же имя в методе совета, из которого определяется тип аргумента. Мы можем использовать общие объекты также в аргументах совета. Код 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()));
}
//Advice arguments, will be applied to bean methods with single String argument
@Before("args(name)")
public void logStringArguments(String name){
System.out.println("String argument passed="+name);
}
}
Пример Spring AOP после консультации
Давайте рассмотрим простой класс аспектов на примере советов After, After Throwing и After Return. Код 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, чтобы продемонстрировать использование совета After Throwing.
Пример Spring AOP вокруг аспекта
Как объяснялось ранее, мы можем использовать аспект вокруг, чтобы сократить выполнение метода до и после. Мы можем использовать его, чтобы контролировать, будет ли выполняться рекомендуемый метод или нет. Мы также можем проверить возвращаемое значение и изменить его. Это самый действенный совет, и его нужно правильно применять. Код 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 в качестве аргумента, и мы должны использовать его метод continue() для вызова метода, рекомендованного целевому объекту. Если рекомендуемый метод что-то возвращает, ответственность за возврат этого вызывающей программе лежит на совете. Для пустых методов метод совета может возвращать ноль. Поскольку рекомендации вокруг обходятся с рекомендуемым методом, мы можем контролировать ввод и вывод метода, а также его поведение при выполнении.
Spring Advice с пользовательской аннотацией Pointcut
Если вы посмотрите на все вышеприведенные выражения pointcut с рекомендациями, есть вероятность, что они будут применены к некоторым другим компонентам, где они не предназначены. Например, кто-то может определить новый компонент Spring с помощью метода getName(), и к нему начнут применяться советы, даже если они не были предназначены. Вот почему мы должны максимально сузить область действия выражения pointcut. Альтернативный подход — создать пользовательскую аннотацию и аннотировать методы, к которым мы хотим применить рекомендации. Это цель метода Employee setName(), аннотированного с помощью Spring Transaction Management. 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
Я всегда предпочитаю аннотацию, но у нас также есть возможность настроить аспекты в файле конфигурации spring. Например, предположим, что у нас есть класс, как показано ниже. Код EmployeeXMLConfigAspect.java:
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 понятно из их названия, поэтому я не буду вдаваться в подробности.
Весенний пример АОП
Давайте создадим простую программу на 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)
Вы можете видеть, что советы выполняются один за другим в зависимости от их конфигураций pointcut. Вы должны настроить их один за другим, чтобы избежать путаницы. Это все, что касается учебника Spring AOP Example Tutorial, я надеюсь, что вы изучили основы AOP с помощью Spring и сможете узнать больше на примерах. Загрузите образец проекта по ссылке ниже и поэкспериментируйте с ним.
Скачать проект Spring AOP