Sure,下面是针对Spring使用AspectJ开发AOP的完整攻略:
一、背景
在面向对象编程中,我们通常使用继承和接口来实现模块化设计和代码重用,但是有些横切性质的问题(例如日志记录、安全、事务等)往往会分散在不同的模块和方法中,难以实现代码重用,这时候就需要AOP(Aspect Oriented Programming)的帮助。
Spring框架整合了AOP机制,通过AspectJ来实现AOP,进一步简化了Spring中AOP的使用。除了XML配置之外,我们还可以通过注解的方式来实现AOP。
二、开发流程
开发Spring服务时,我们可以按照以下步骤来使用AspectJ实现AOP:
1.添加依赖
首先需要向项目的pom.xml文件中添加以下依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
其中,spring-aop是Spring框架的AOP模块,aspectjweaver则是AspectJ的核心库。
2.定义切面
AspectJ中的切面(Aspect)类是一个Java类,需要使用@Aspect注解进行标注。切面类中定义的方法可以分别在目标方法执行前、执行后或异常抛出时执行,并可以将其称为切点(Pointcut)。下面是一个例子:
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class LoggingAspect {
@Pointcut("execution(* com.example.*.*(..))")
public void loggingPointcut() {}
@AfterReturning("loggingPointcut()")
public void logAfterReturning() {
// Logging statements go here
}
}
上面的例子定义了一个名为LoggingAspect的切面类,其中声明了一个名为loggingPointcut的切点,这个切点用于捕获所有com.example包中的方法调用;另外,切面中还定义了一个名为logAfterReturning的通知(Advice),用于在loggingPointcut匹配的所有目标方法正常返回后执行。
3.定义通知
切面类中的方法有以下几种通知类型:
- Before:在目标方法执行前执行
- After:在目标方法执行后执行
- AfterReturning:在目标方法正常返回后执行
- AfterThrowing:在目标方法抛出异常后执行
- Around:可以在目标方法执行前后及异常抛出时执行预先处理或者修改方法执行的参数和返回值
通知方法必须使用@AfterReturning、@Before、@After、@Around、@AfterThrowing中的一种或者是多个修饰,以表示通知的作用和类型。
下面是一个Around通知的例子:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TimeLoggingAspect {
@Around("execution(* com.example.*.*(..))")
public Object logAroundMethod(ProceedingJoinPoint jointPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = jointPoint.proceed();
long endTime = System.currentTimeMillis();
String methodName = jointPoint.getSignature().getName();
String className = jointPoint.getTarget().getClass().getName();
System.out.println("执行" + className + "类中的" + methodName + "方法耗时" + (endTime - startTime) + "ms");
return result;
}
}
这个例子定义了一个名为TimeLoggingAspect的切面类,其中声明了一个Around类型的通知方法logAroundMethod,用于在loggingPointcut匹配的所有目标方法执行前后统计它们的执行时间。
4.配置AOP
最后,我们需要配置Spring的AOP模块,使其使用AspectJ切面:
<aop:aspectj-autoproxy />
<bean id="loggingAspect" class="com.example.LoggingAspect" />
<bean id="timeLoggingAspect" class="com.example.TimeLoggingAspect" />
上面的XML配置文件中,aop:aspectj-autoproxy
是用于开启AspectJ自动代理的,而bean标签则是声明了我们定义的两个切面类。
三、示例说明
下面我们通过两个简单的示例说明如何使用AspectJ来开发AOP。
例1:为类的方法添加日志记录
public class MyService {
public void doSomething() {
System.out.println("doing something...");
}
}
在这个例子中,我们希望在该方法执行前后记录日志。
Step1:定义切面
@Aspect
public class LoggingAspect {
@Pointcut("execution(* com.example.MyService.*(..))")
public void loggingPointcut() {}
@Before("loggingPointcut()")
public void logBefore() {
System.out.println("方法执行前记录日志");
}
@After("loggingPointcut()")
public void logAfter() {
System.out.println("方法执行后记录日志");
}
}
上述代码定义了切点loggingPointcut
,它匹配了所有MyService
类中的方法调用。同时,定义了logBefore()
和logAfter()
两个通知方法用于分别在方法执行前后记录日志。
Step2:配置AOP
<bean id="myService" class="com.example.MyService"/>
<bean id="loggingAspect" class="com.example.LoggingAspect"/>
<aop:aspectj-autoproxy />
上述代码中,我们分别将MyService和LoggingAspect声明为bean。同时,需要开启AspectJ自动代理。
Step3:测试
MyService myService = (MyService) context.getBean("myService");
myService.doSomething();
这个例子中,我们通过IoC容器获取到myService对象,调用doSomething()方法来触发AOP切面的执行。
例2:添加方法执行时间统计
public class MyService {
public void doSomething() {
System.out.println("doing something...");
}
}
在这个例子中,我们希望记录MyService类中的所有方法的执行时间。
Step1:定义切面
@Aspect
public class TimeLoggingAspect {
@Around("execution(* com.example.MyService.*(..))")
public Object logAroundMethod(ProceedingJoinPoint jointPoint) throws Throwable {
long startTime = System.currentTimeMillis();
// 调用目标方法
Object result = jointPoint.proceed();
long endTime = System.currentTimeMillis();
// 获取方法名和类名
String methodName = jointPoint.getSignature().getName();
String className = jointPoint.getTarget().getClass().getName();
System.out.println("执行" + className + "类中的" + methodName + "方法耗时" + (endTime - startTime) + "ms");
return result;
}
}
上述代码定义了名为TimeLoggingAspect的切面类。其中,使用了@Around注解修饰的方法名为logAroundMethod()
。
Step2:配置AOP
<bean id="myService" class="com.example.MyService"/>
<bean id="timeLoggingAspect" class="com.example.TimeLoggingAspect"/>
<aop:aspectj-autoproxy />
上述代码中,我们同样分别将MyService和TimeLoggingAspect声明为bean,同时,需要开启AspectJ自动代理。
Step3:测试
MyService myService = (MyService) context.getBean("myService");
myService.doSomething();
与前一个例子类似,我们通过IoC容器获取到myService对象,调用doSomething()方法来触发AOP切面的执行。
四、总结
Spring的AOP机制可以通过AspectJ实现,这也是Spring AOP的主要实现方式之一。通过使用AspectJ的注解来定义切面和通知,我们可以在切面中对方法调用进行拦截和处理,这种方式大量减少了AOP的代码量。
在实际开发中,AOP的使用场景非常广泛。例如,我们可以通过AOP添加日志记录和性能统计,实现数据权限控制和事务管理等。以上是使用AspectJ的AOP实现的基本流程和两个示例。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring用AspectJ开发AOP(基于Annotation) - Python技术站