版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/j1231230/article/details/81837022 </div>
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/ck_htmledit_views-cd6c485e8b.css">
<div id="content_views" class="markdown_views">
<!-- flowchart 箭头图标 勿删 -->
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
</svg>
<p>AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。一般来说,我们项目中的日志几乎会遍布所有controller,不利于统一管理,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。</p>
使用”横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
接下来,我们看一下如何使用AOP给springboot项目添加日志:
一.引入AOP的依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 1
- 2
- 3
- 4
二.添加AOP的全局类
@Aspect
@Component
public class WebLogAspect {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private LogService logService;
@Autowired
private UserService userService;
private String url;
private String ip;
private Integer port;
private User user;
@Pointcut("execution(public * com.ytint.bb6.controller..*.*(..))")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录请求内容,这里只记录非GET请求的日志信息
if (!request.getMethod().equals("GET")) {
logger.info("请求URL: " + request.getRequestURL().toString());
url = request.getRequestURI();
ip = request.getRemoteHost();
port = request.getRemotePort();
user = userService.getCurrentUser();
}
}
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
Integer logType = LogType.OPERATION.getId();
if (ret instanceof ResultInfo) {
ResultInfo result = (ResultInfo) ret;
String msg = result.getMsg();
String module = result.getModule();
Integer code = result.getCode();
if (UtilValidate.isNotEmpty(msg)) {
logger.info(msg);
// 操作模块返回200才存储到日志表
if (code.equals(200)) {
logService.save(user, ip, port, msg, logType, module, url);
}
}
}
}
@AfterThrowing(pointcut = "webLog()", throwing = "e")
public void doException(JoinPoint jp, Throwable e) {
if (e != null) {
Logger logger = LoggerFactory.getLogger(jp.getSignature().getClass());
logger.error("程序发生了异常:" + e.getMessage(), e);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
Spring使用AspectJ注解来声明通知方法
@After:会在目标方法返回或抛出异常后调用
@AfterReturning:会在目标方法返回后调用
@AfterThrowing:会在目标方法抛出异常后调用
@Around:会将目标方法封装起来
@Before:会在目标方法调用之前执行
使用@Pointcut定义切面
@Pointcut后面括号里的内容是切面表达式:
@Pointcut("execution(public * com.yt.controller..*.*(..))")
- 1
表达式含义如下:
1) execution(): 表达式主体;
2) public *:表示返回类型, *号表示所有的类型;
3) 包名:表示需要拦截的包名,后面的两个句点表示当前包和当前包的所有子包,com.yt.controller包、子孙包下所有类的方法;
4) 第二个*号:表示类名,*号表示所有的类;
5) *(..):表示方法名,*号表示所有的方法,括号里面表示方法的参数,..表示任何参数;
webLog()方法定义了一个可重用的切点,这样我们就不必把同样的切点表达式傻傻的写好多遍了,只要在每次需要的时候引用它就可以了,webLog方法的实际内容并不重要,他本身只是一个标识,供@Pointcut注解依附。
在日志中添加模块信息
如果我们不只想获取当前方法的URL,参数等基本信息,还想获取它来源于某一个controller模块该怎么办呢,我这里采用了一个有点傻的方法,可以在返回的结果对象中添加一个模块字段module,把对应的controller信息放在模块里,这样输出日志时就可以知道当前请求是来源于哪个controller了。如果大家有更好的方式,欢迎交流~
本篇博客部分参考了以下两篇博客,送上花花~~~
https://www.cnblogs.com/hongwz/p/5764917.html
https://www.cnblogs.com/30go/p/8443522.html