Aop简介
Aop(Aspect Oriented Programming的缩写)面向切面编程,Spring 框架中一个重要的模块。
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Aop的通知类型
- 前置通知
- 后置通知
- 异常通知
- 最终通知
- 环绕通知
术语词说明
- Target(目标对象):访问真实主题对象,就是目标对象。
- Proxy(代理对象):在访问目标对象之前或者之后,要执行的操作,该操作可以放置到代理对象中完成。
- joinpoint(连接点):访问目标对象中的所有方法,该方法就是连接点
- Pointcut(切入点):访问目标对象的某些需要被特殊处理的方法(也就是需要进行拦截的方法),在代理对象中被拦截的方法就是切入点
注意;由连接点和切入点的概念可以推导出切入点应该是连接点的一个子集。 - Advice(通知):切入点要做的事情,就是通知,通知也泛指方法
- Aspect(切面):通知要放置到类中,该类就是切面
- Weaving(织入):是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象
- Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Aop作用:
Aop可以将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
应用场景:
- 日志记录,跟踪,优化和监控
- 事务的处理
- 持久化
- 性能的统计
- 资源池,如数据库连接池的管理
- 系统统一的认证、权限管理等
- 应用系统的异常捕捉及处理
执行顺序实践
目录
1. 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 编写API接口
@RestController
public class IndexController {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexController.class);
@GetMapping(value = "/index")
public String index(){
LOGGER.info(">>> index");
return "index";
}
}
3.编写Aop代码
@Component
@Aspect
public class AspectLog {
private static final Logger LOGGER = LoggerFactory.getLogger(AspectLog.class);
@Pointcut(value = "execution(public * com.lgs.controller..*(..))")
public void point() {
}
@Before(value = "point()")
public void beforeMethod1() throws InterruptedException {
LOGGER.info(">>> 前置通知");
}
@After(value = "point()")
public void afterMethod() {
LOGGER.info(">>> 后置通知");
}
@AfterReturning(value = "point()", returning = "object")
public void afterReturningMethod(Object object) {
LOGGER.info(">>> 后置返回通知");
}
@AfterThrowing(value = "point()", throwing = "exce")
public void afterThrowingMethod(Exception exce) {
LOGGER.info(">>> 异常通知");
}
@Around(value = "point()")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
LOGGER.info(">>> 环绕通知开始");
Object proceed = joinPoint.proceed();
LOGGER.info(">>> 环绕通知结束");
return proceed;
}
}
Execution表达式:
execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?)
修饰符模式 和 异常模式 是可选的,其它的必填。
4.启动SpringApplication
访问:localhost:8080/index
控制台输出:
5.在业务中添加异常代码,测试异常情况下执行顺序
@RestController
public class IndexController {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexController.class);
@GetMapping(value = "/index")
public String index() {
int i = 1 / 0;
LOGGER.info(">>> index");
return "index";
}
}
6.运行结果
控制台输出:
总结
业务正常情况下执行顺序
环绕通知开始
前置通知
执行业务代码
环绕通知执行结束
后置通知
返回通知
业务异常情况下执行顺序
环绕通知开始
前置通知
后置通知
异常通知
统一处理Web请求日志
编写切面代码
@Component
@Aspect
public class AspectLog {
private static final Logger LOGGER = LoggerFactory.getLogger(AspectLog.class);
@Pointcut(value = "execution(public * com.lgs.controller..*(..))")
public void point() {
}
@Before(value = "point()")
public void beforeMethod1(JoinPoint joinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String requestURL = request.getRequestURL().toString();
String method = request.getMethod();
String remoteAddr = request.getRemoteAddr();
String params = Arrays.asList(joinPoint.getArgs()).toString();
LOGGER.info("requestURL:{}, method:{}, remoteAddr:{}, params:{}", requestURL, method, remoteAddr, params);
}
@AfterReturning(value = "point()", returning = "object")
public void afterReturningMethod(Object object) {
LOGGER.info(">>> 返回结果:{}", object.toString());
}
}
运行程序,访问接口
控制台输入日志:
源码:https://git.dev.tencent.com/lgs_/spring-boot-parent.git