1、aop的使用思想:面向切面的编程,不改变原有代码的基础上,进行拓展,减少代码的冗余,降低耦合性;
2、使用注解进行aop编程,使用自定义注解
2.1导入aop的依赖
<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>
2.2新建注解类:TestCut
package com.example.demo.annotations;
import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestCut {
String module() default "测试";
String descript() default "描述";
}
2.3新建切面类:TestCutAspect
package com.example.demo.aop;
import com.example.demo.annotations.TestCut;
import com.example.demo.common.RestResponse;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Aspect
public class TestCutAspect {
@Pointcut("@annotation(com.example.demo.annotations.TestCut)")
public void testCut(){
}
@Before("testCut()")
public void beforeFun(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println(arg.toString());
}
//请注意,这种修改是在切面内部进行的,不会影响原始方法中的参数值。
// 如果你想要修改的是传入原方法的实际参数,而不仅仅是在切面中使用的参数的副本,那么这种方式可能不适用,
// 因为join point提供的参数是只读的。在这种情况下,你可能需要考虑使用@Around建议来完全控制方法的执行,包括参数的传递。
args[0]="789";
System.out.println("注解方式AOP开始拦截, 当前拦截的方法名: " + method.getName());
}
@After("testCut()")
public void afterFun(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP执行的方法 :" + method.getName() + " 执行完了");
}
//1、注意一定要将@Around修饰的方法用Object修饰其返回值,并且返回原方法执行的结果。
//2、@Around比较万能,尤其是配合ProceedingJoinPoint的使用。使AOP能做的事情更多了。
//3、@Around如果不执行proceed(),那么原方法将不会执行
//4、ProceedingJoinPoint 只能在@Around中使用
@Around("testCut()")
public Object testCutAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("注解方式AOP拦截开始进入环绕通知.......");
Object[] args = joinPoint.getArgs();
//修改args的值,相当于篡改了原方法入参的参数值
args[0] ="***";
//获取注解属性
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature =(MethodSignature)signature;
Method method = methodSignature.getMethod();
TestCut annotation = method.getAnnotation(TestCut.class);
String descript = annotation.descript();
String module = annotation.module();
System.out.println(module+"|||"+descript);
//执行原方法
Object proceed = joinPoint.proceed(args);
//修改proceed的值,相当于篡改了原方法执行完成之后的返回值
proceed = RestResponse.error();
System.out.println("准备退出环绕......");
return proceed;
}
/**
* returning属性指定连接点方法返回的结果放置在result变量中
*
* @param joinPoint 连接点
* @param result 返回结果
*/
@AfterReturning(value = "testCut()", returning = "result")
public void afterReturn(JoinPoint joinPoint, Object result) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP拦截的方法执行成功, 进入返回通知拦截, 方法名为: " + method.getName() + ", 返回结果为: " + result.toString());
}
@AfterThrowing(value = "testCut()", throwing = "e")
public void afterThrow(JoinPoint joinPoint, Exception e) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("注解方式AOP进入方法异常拦截, 方法名为: " + method.getName() + ", 异常信息为: " + e.getMessage());
}
}
2.4新建测试类,在需要切面的方法添加注解@TestCut(module = "测试aop",descript = "我是aop的描述属性")
@RestController
@RequestMapping("/aop")
public class AopRestController {
@RequestMapping("/test")
@TestCut(module = "测试aop",descript = "我是aop的描述属性")
public RestResponse test(String aaa,String bbb){
System.out.println("我是test方法");
String c = aaa+bbb;
return RestResponse.successMsg(c);
}
}
2.5定义统一返回结果
package com.zlkj.file.exception;
import io.swagger.annotations.ApiModelProperty;
/**
* @author ycy
*/
public class RestResponse {
@ApiModelProperty(
name = "success",
value = "是否成功",
required = true,
dataType = "Boolean"
)
private Boolean success;
@ApiModelProperty(
name = "data",
value = "返回结果",
required = false,
dataType = "Object"
)
private Object data;
@ApiModelProperty(
name = "msg",
value = "提示信息",
required = false,
dataType = "String"
)
private String msg;
public RestResponse(Boolean success, String msg, Object data){
this.success = success;
this.msg = msg;
this.data = data;
}
public static RestResponse success(){
return new RestResponse(true, null,null);
}
public static RestResponse error(){
return new RestResponse(false, null, null);
}
public static RestResponse successMsg(String msg){
return new RestResponse(true, msg, null);
}
public static RestResponse errorMsg(String msg){
return new RestResponse(false, msg, null);
}
public static RestResponse successResult(String msg, Object data){
return new RestResponse(true, msg, data);
}
public static RestResponse errorResult(String msg, Object data){
return new RestResponse(false, msg, data);
}
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "RestResponse{" +
"success=" + success +
", data=" + data +
", msg='" + msg + '\'' +
'}';
}
}
3、项目结构
4、执行接口
5、通过测试发现常用的aop注解的执行顺序,around包在最外侧;
@around-方法前
@before
@afterReturning
@after
@around-方法后
有异常:
无异常:
通过测试看就是:
1、afterReturning和afterThrow是互斥的二选一;
2、after一定会执行,around后置方法在抛出异常之后不会执行了;
6、around可以修改入参、出参的值;
注意一定要将@Around修饰的方法用Object修饰其返回值,并且返回原方法执行的结果。
@Around比较万能,尤其是配合ProceedingJoinPoint的使用。使AOP能做的事情更多了。
@Around如果不执行proceed(),那么原方法将不会执行
ProceedingJoinPoint 只能在@Around中使用