AOP(Aspect Oriented Programming),意为:面向切面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。
AOP的编程思想就是把很多类对象中的横切问题点,从业务逻辑中分离出来,从而达到解耦的目的增加代码的重用性,提高开发效率。
主要是非业务代码抽取的问题。
遇到下面这种情况一般处理的方法是提取出公共方法类,然后调用
但是产生的问题
很多类对象和Logger仍然存在依赖关系
甚至每个类中的所有方法都需要调用Logger
如果修改了方法,需要修改N遍,不能一次修改完成改变
AOP一般处理的八大场景
日志记录 异常处理
权限验证 缓存处理
事务处理 数据持久化
效率检查 内容分发
AOP五个注解的执行顺序
在一个方法只被一个aspect类拦截时,aspect类内部的 advice 将按照以下的顺序进行执行:
正常:
异常
1.创建工程并在pom中添加引用
<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.方便演示,写一段被切入的controller方法
package com.example.aopdemo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AopTestController {
//http://localhost:8080/hello?name=zzz
@GetMapping("hello")
public String hello(@RequestParam("name") String name){
return "hello"+name;
}
//http://localhost:8080/hello1?name=zzz&age=18
@GetMapping("hello1")
public String hello1(@RequestParam("name") String name,@RequestParam("age") String age){
return "hello"+name+"age"+age;
}
}
3.Aspect驱动,定义一个切面类
package com.example.aopdemo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* 1.定义一个日志切面类Aspect
* 即在声明的类,增加@Component @Aspect 两个注解,Springboot中要引入 spring-boot-stater-aop依赖包
*/
@Aspect
@Component
public class MyAdvice {
public Logger logger = LoggerFactory.getLogger(MyAdvice.class);
/**
* 2:定义切点 Pointcut
* 定义切点,并定义切点在那些地方执行,采用@Pointcut注解完成,如@Pointcut( public * com.xxx.xxx.*.*(..))
* 规则:修饰符(一般是public,可以不写,但不能用*)+返回类型+哪些包下的类+那些方法+方法参数“”代表不限,“”两个点代表参数不限
* 此处的第一个*是返回类型不限 第二个*是目录下所有的类,第三个是所有方法,".."是参数不限
*/
@Pointcut("execution(* com.example.aopdemo.controller.*.*(..) )")
public void myCut() {
}
/**
*定义Advice 通知
* 利用通知的5种类型注解@Before、@After、@AfterReturnina、@AfterThrowina、@Around来完成在某些切点的增强动作
*
* Before advice
* 前置通知,即在目标方法调用之前执行。注意:即无论方法是否遇到异常都执行
*
* After returning advice
* 后置通知,在目标方法执行后执行,前提是目标方法没有遇到异常,如果有异常则不执行通知
*
* After throwing advice
* 异常通知,在目标方法抛出异常时执行,可以获取异常信息
*
* After finally advice
* 最终通知,在目标方法执行后执行,无论是否是异常执行。
*
* Around advice
* 环绕通知,最强大的通知类型,可以控制目标方法的执行
* (通过调用ProceedingJoinPoint.proceed()),可以在目标执行全过程中进行执行。
*
* 同时Around入参是ProceedingJoinPoint,其他的是JoinPoint,ProceedingJoinPoint继承的JoinPoint
*/
@Around("myCut()")
public Object logWriteAround(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().toString();
String methodName = joinPoint.getSignature().getName();
Object [] array= joinPoint.getArgs();
ObjectMapper objectMapper = new ObjectMapper();
logger.info("Around调用前:" + className + ":" + methodName + "传递参数为:" + objectMapper.writeValueAsString(array));
Object afterObject = joinPoint.proceed();
logger.info("Around调用后:" + className + ":" + methodName + "传递参数为:" + objectMapper.writeValueAsString(afterObject));
return afterObject;
}
@Before("myCut()")
public void logWriteBefore(JoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().toString();
String methodName = joinPoint.getSignature().getName();
Object [] array= joinPoint.getArgs();
ObjectMapper objectMapper = new ObjectMapper();
logger.info("Before调用前:" + className + ":" + methodName + "传递参数为:" + objectMapper.writeValueAsString(array));
}
}
执行情况