小白的springboot之路(十四)、AOP
2024-09-01 20:54:55
0、前言
1、什么是AOP
AOP(面向切面编程),是一种横切技术,是对OOP的补充和完善;
使用AOP的横切,可以对系统进行无侵入性的日志监听、事务、权限管理等;
思想上跟拦截器其实类似;拦截器是对action进行拦截处理,AOP是对切面进行拦截处理,其实切面也属于一种action集合;
AOP可以很好解耦;
2、AOP的组成
Aspect:切面;
Join point:连接点;
Advice:通知,在切入点上执行的操作;
Poincut:带有通知的连接点;
target:被通知的对象;
AOP proxy;AOP代理;
其中,Advice(通知)分为以下几种:
- before(前置通知): 在方法开始执行前执行
- after(后置通知): 在方法执行后执行
- afterReturning(返回后通知): 在方法返回后执行
- afterThrowing(异常通知): 在抛出异常时执行
- around(环绕通知): 在方法执行前和执行后都会执行
通知的执行顺序:
around > before > around > after > afterReturning
一、实现示例
光看理论和定义,很多人可能都觉得很难理解,其实用法比较简单,不难的,
我们先来个简单的例子,看完例子你可能就豁然开朗了,
所谓程序员,好看书不如多动手:
实现:
1、添加依赖
<!-- 8、集成AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、添加切面类LogAspect
package com.anson.common.aspect; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest;
import java.util.Arrays; /**
* @description: AOP切面
* @author: anson
* @Date: 2019/12/20 10:11
*/ @Aspect //1、添加AOP相关注解
@Component
public class LogAspect
{
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); //2、定义切入点(可以匹配、注解的方式,可混用)
// @Pointcut("execution(public * com.anson.controller.*.*(..))")
@Pointcut("execution(public * com.anson.controller.*.*(..)) && @annotation(com.anson.common.annotation.LogAnnotation)")
// @Pointcut("execution(public * com.anson.controller.TestController.get*()) && @annotation(com.anson.common.annotation.LogAnnotation)")
public void pointcut(){} //===========通知 多中通知可根据需要灵活选用,一般Before 、AfterReturning即可=======================
/**
* 前置通知:在连接点之前执行的通知
* @param joinPoint
* @throws Throwable
*/
@Before("pointcut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest(); // 记录下请求内容
logger.info("URL : " + request.getRequestURL().toString());
logger.info("HTTP_METHOD : " + request.getMethod());
logger.info("IP : " + request.getRemoteAddr());
logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
} @AfterReturning(returning = "ret",pointcut = "pointcut()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
logger.info("RESPONSE : " + ret);
}
//==============================================
@After("pointcut()")
public void commit() {
logger.info("after commit");
} @AfterThrowing("pointcut()")
public void afterThrowing() {
logger.info("afterThrowing afterThrowing rollback");
} @Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
System.out.println("around");
return joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
throw e;
} finally {
logger.info("around");
}
} }
需要注意的是:上面代码注释2的地方【2、定义切入点(可以匹配、注解的方式,可混用)】;
简单点说就是通过匹配或者注解(也可两种同时使用)匹配哪些类的哪些方法;
再直白点说,就是我们要对哪些类的哪些方法执行处理,要处理的范围是哪些;
类用*作为通配符,方法用(..)表示,括号中的两点表示匹配任何参数,包括没有参数;
上面这样其实就可以完成一个切面了,
比如将切面定义那里改为:@Pointcut("execution(public * com.anson.controller.*.*(..))")
那么运行程序后,所有com.anson.controller包下的所有类的所有方法,都会执行这个切面定义的方法进行相关写日志处理了,结果如下:
around
[2019-12-20 11:19:02,205][INFO ][http-nio-8095-exec-1] URL : http://localhost:8095/user/userall (LogAspect.java:45)
[2019-12-20 11:19:02,205][INFO ][http-nio-8095-exec-1] HTTP_METHOD : GET (LogAspect.java:46)
[2019-12-20 11:19:02,206][INFO ][http-nio-8095-exec-1] IP : 0:0:0:0:0:0:0:1 (LogAspect.java:47)
[2019-12-20 11:19:02,207][INFO ][http-nio-8095-exec-1] CLASS_METHOD : com.anson.controller.UserController.getUserAll (LogAspect.java:48)
[2019-12-20 11:19:02,208][INFO ][http-nio-8095-exec-1] ARGS : [] (LogAspect.java:49)
[2019-12-20 11:19:02,510][DEBUG][http-nio-8095-exec-1] ==> Preparing: select id, userName, passWord, realName from user (BaseJdbcLogger.java:143)
[2019-12-20 11:19:02,606][DEBUG][http-nio-8095-exec-1] ==> Parameters: (BaseJdbcLogger.java:143)
[2019-12-20 11:19:02,631][DEBUG][http-nio-8095-exec-1] <== Total: 4 (BaseJdbcLogger.java:143)
[2019-12-20 11:19:02,634][INFO ][http-nio-8095-exec-1] around (LogAspect.java:77)
[2019-12-20 11:19:02,635][INFO ][http-nio-8095-exec-1] after commit (LogAspect.java:60)
[2019-12-20 11:19:02,635][INFO ][http-nio-8095-exec-1] RESPONSE : com.anson.common.result.ResultBody@6d9947d (LogAspect.java:55)
如果不用注解的话,上面就已经完成一个切面了,如果用注解来定义切面范围呢,好,也简单,我们来定义一个注解 --------------华丽丽的分割线-------------------------------
--------------增加自定义注解的方式----------------------------
3、添加一个LogAnnotation注解
package com.anson.common.annotation; import java.lang.annotation.*; /**
* @description: 自定义注解
* @author: anson
* @Date: 2019/12/20 10:32
*/ @Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAnnotation
{ }
这样就可以了,然后,在需要的地方,加入这个自定义注解:
//2、获取所有用户
@ApiOperation(value = "获取所有用户", notes = "获取所有用户")
@RequestMapping(value="/userall",method= RequestMethod.GET)
@LogAnnotation //自定义注解
public ResultBody getUserAll()
{
List<User> users = userservice.getAll();
return ResultBody.success(users,"获取所有用户信息成功");
}
同时,修改切面范围的定义即可:
//2、定义切入点(可以匹配、注解的方式,可混用)--以下表示范围为:controller包下所有包含@LogAnnotation注解的方法
@Pointcut("execution(public * com.anson.controller.*.*(..)) && @annotation(com.anson.common.annotation.LogAnnotation)") public void pointcut(){}
完了,就这么简单;
至于什么地方该使用AOP,以及AOP和拦截器用哪个比较好,这个就要根据业务场景灵活取舍了,掌握了思想,具体使用那就可以灵活发挥了
最新文章
- Linux命令總結
- Window Server 2012 R2 没有照片查看器 打开图片都是画板问题怎么解决
- React Native iOS环境搭建
- 【转】linux dumpe2fs命令
- Helpers\Password
- 【codeforce 219D】 Choosing Capital for Treeland (树形DP)
- python与编码方式
- 【SICP读书笔记(二)】使用过程来黏合数据 —— 酷炫吊的消息传递机制
- 【NOIP2012】DAY1+DAY2题解
- OOP in JS Public/Private Variables and Methods
- poj3207
- 关于Python中yield的一些个人见解
- 3分钟anaconda安装Python框架Tensorflow,1行语句测试是否成功?
- pycharm 如何进行全部搜索
- 以前的 Delphi版本
- .net core中Quartz的使用
- nRF51 DK : nRF51822 Development Kit for Bluetooth Smart, ANT and 2.4GHz applications.
- window 10 企业版激活
- Android Binder分析二:Natvie Service的注冊
- Mininet实验 设置带宽之简单性能测试