一、准备
1.springboot的demo项目一个。
2.maven的xml配置 ,
spring-boot-starter-web:springboot启动需要的类。
spring-boot-starter-aop:开启aop,必需有,没有完成不了切面。
aspectjrt:切面所需要的类和注解都在里面。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
二、实现
1.自定义一个注解
import java.lang.annotation.*;
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
public @interface DS {
String value() default "";
}
2.写一个切面类
只要方法上有@DS自定义注解的方法都会被切面类检测到。
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@Component
public class AspectAnnotation {
/**
* 使用@Before在切入点开始处切入内容
* @param point
*/
@Before("@annotation(DS)")
public void Before(JoinPoint point){
System.out.println("在切入点开始处切入内容");
}
/**
* 使用@After在切入点结尾处切入内容
*/
@After("@annotation(DS)")
public void After(JoinPoint point){
System.out.println("在切入点结尾处切入内容");
}
/**
* 使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
*/
@AfterReturning("@annotation(DS)")
public void AfterReturning(JoinPoint point){
System.out.println("在切入点return内容之后切入内容");
}
/**
* 使用@Around在切入点前后切入内容,并自己控制何时执行切入点自身的内容(环绕通知)
*/
@Around("@annotation(DS)")
public Object Around(ProceedingJoinPoint pdj){
/*result为连接点的放回结果*/
Object result = null;
String methodName = pdj.getSignature().getName();
/*前置通知方法*/
System.out.println("1前置通知方法>目标方法名:" + methodName + ",参数为:" + Arrays.asList(pdj.getArgs()));
/*执行目标方法*/
try {
result = pdj.proceed();
/*返回通知方法*/
System.out.println("1返回通知方法>目标方法名" + methodName + ",返回结果为:" + result);
} catch (Throwable e) {
/*异常通知方法*/
System.out.println("1异常通知方法>目标方法名" + methodName + ",异常为:" + e);
}
/*后置通知*/
System.out.println("1后置通知方法>目标方法名" + methodName);
return result;
}
/**
* 使用@AfterThrowing用来处理当切入内容部分抛出异常之后的处理逻辑
*/
@AfterThrowing("@annotation(DS)")
public void AfterThrowing(JoinPoint point){
System.out.println("用来处理当切入内容部分抛出异常之后的处理逻辑");
}
}
3.写一个测试类,加上DS注解。
import com.xx.job.demo.Test01;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AOPContrller {
@DS("dddd")
@GetMapping("/demo")
public void test(String name,int age){
System.out.println("demo");
}
}
这样一个简单的切面测试就完成了。
三、问题
1.@Before、@After、@AfterReturning、@AfterThrowing和@Around一起使用时,@Around里面什么都不做其它几个注解不会执行的。
答:@Around环绕通知,跟其它几个注解不能。@Before、@After、@AfterReturning、@AfterThrowing不会管方法的执行,只会检测方法的执行前后。而@Around直接拦截方法,而且控制切面的方法是否执行,只有@Around里面通过ProceedingJoinPoint的方法
public Object proceed() throws Throwable;执行了切面方法,其它几个注解才会生效。
2.项目启动报错 ProceedingJoinPoint is only supported for around advice
答:ProceedingJoinPoint这个类只能在@Around里面做为参数,@Before、@After、@AfterReturning、@AfterThrowing用JoinPoint。看一下ProceedingJoinPoint源码,继承JoinPoint只有两个方法,方法作用就是执行切面方法。只有在@Around环绕通知里面才需要执行方法。
package org.aspectj.lang;
import org.aspectj.runtime.internal.AroundClosure;
/**
* ProceedingJoinPoint exposes the proceed(..) method in order to support around advice in @AJ aspects
*
* @author Alexandre Vasseur
*/
public interface ProceedingJoinPoint extends JoinPoint {
/**
* Proceed with the next advice or target method invocation
*
* @return the result of proceeding
* @throws Throwable if the invoked proceed throws anything
*/
public Object proceed() throws Throwable;
/**
* @param args the arguments to proceed with
* @return the result of proceeding
* @throws Throwable if the invoked proceed throws anything
*/
public Object proceed(Object[] args) throws Throwable;
}
四、JoinPoint提供的API
1.Signature getSignature();获取连接点的方法签名对象;也就是切面方法的信息
2.Object[] getArgs();获取连接点方法运行时的入参列表; 切面方法的参数
3.String getKind(); 返回一个表示连接点类型的字符串。
五、ProceedingJoinPoint提供的API
1.public Object proceed() throws Throwable;执行切面方法
2.public Object proceed(Object[] args) throws Throwable;带参数执行切面方法