AOP 面向切面 记录请求接口的日志
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
在spring AOP中业务逻辑仅仅只关注业务本身,将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
相关注解介绍:
@Aspect:作用是把当前类标识为一个切面供容器读取
@Pointcut:Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around:环绕增强,相当于MethodInterceptor
@AfterReturning:后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before:标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing:异常抛出增强,相当于ThrowsAdvice
@After: final增强,不管是抛出异常或者正常退出都会执行
Spring AOP 中@Pointcut的用法
execution表达式
1)execution(* *(..))
//表示匹配所有方法
2)execution(public * com. xl.service.UserService.*(..))
//表示匹配com.xl.server.UserService中所有的公有方法
3)execution(* com.xl.server..*.*(..))
//表示匹配com.xl.server包及其子包下的所有方法
4)@annotation(com.platform.annotation.SysLog)
//表示匹配注解SysLog
在Spring 2.0中,Pointcut的定义包括两个部分:Pointcut表示式(expression)和Pointcut签名(signature)
//Pointcut表示式
@Pointcut("execution(* com.xl.aop.MessageSender.*(..))")
//Point签名
private void log(){}
然后要使用所定义的Pointcut时,可以指定Pointcut签名
如下:
@Before("log()")
这种使用方式等同于以下方式,直接定义execution表达式使用
@Before("execution(* com.xl.aop.MessageSender.*(..))")
请求日志代码:
package cn.aid.cmsweb.assist.aspect; import cn.aid.cmsweb.entity.UserDetails;
import cn.aid.cmsweb.handler.OperationLogHandler;
import cn.aid.common.utils.util.HttpUtil;
import cn.aid.data.api.resource.entity.OperationLog;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiOperation;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*; @Aspect //使之成为切面类
@Component //把切面类加入到IOC容器中
@Order(1) //控制AOP的加载顺序
public class OperationLogAspect {
private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired
OperationLogHandler operationLogHandler; private ThreadLocal<Long> startTime = new ThreadLocal<>(); //定义切入点
@Pointcut("execution(* cn.aid.cmsweb.controller..*.*Controller.*(..))")
public void webLog() {
} //前置通知:目标方法执行之前执行以下方法体的内容
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
} //返回通知:目标方法正常执行完毕时执行以下代码
@AfterReturning(value = "webLog()", returning = "ret")
public void doAfterReturning(Object ret) throws Throwable {
} //环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码
@Around("webLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
Object result = null;
try {
//【环绕通知中的--->前置通知】
//获取当前请求对象
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest(); Map<String, String[]> parameterMap = request.getParameterMap(); //记录请求信息
OperationLog operationLog = new OperationLog();
result = joinPoint.proceed();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method.isAnnotationPresent(ApiOperation.class)) {
ApiOperation log = method.getAnnotation(ApiOperation.class);
operationLog.setDescription(log.value());
} long endTime = System.currentTimeMillis(); UserDetails userDetails = (UserDetails) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT_DETAILS");
if(null != userDetails ){
operationLog.setUsername(userDetails.getUsername());
} operationLog.setBasePath(HttpUtil.getBasePath(request));
operationLog.setIp(HttpUtil.getClientIp(request));
operationLog.setMethod(request.getMethod());
operationLog.setParameter(getParameterMap(parameterMap));
//operationLog.setResult(result);
operationLog.setSpendTime((int) (endTime - startTime.get()));
operationLog.setOperationTime(startTime.get());
operationLog.setUri(request.getRequestURI());
operationLog.setUrl(request.getRequestURL().toString());
// operationLog.setDescription(getParameterMap(parameterMap));
Map<String,Object> logMap = new HashMap<>();
logMap.put("url",operationLog.getUrl());
logMap.put("method",operationLog.getMethod());
logMap.put("parameter",operationLog.getParameter());
logMap.put("spendTime",operationLog.getSpendTime());
logMap.put("description",operationLog.getDescription());
// LOGGER.info("{}", JsonUtil.objectToJson(webLog));
//logger.info(Markers.appendEntries(logMap),JsonUtil.objectToJson(operationLog));
operationLogHandler.saveLog(operationLog);
// 【环绕通知中的--->返回通知】
return result;
} catch (Exception e) {
//【环绕通知中的--->异常通知】
logger.error("日志记录出错!", e);
return result;
}
//【环绕通知中的--->HOUZ通知】 } private String getParameterMap(Map<String,String[]> parameterMap) {
if(null == parameterMap){
return null;
} String result = null;
try{
Map<String,Object> paramsMap = new HashMap<>();
Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet(); entries.forEach( entry -> {
paramsMap.put(entry.getKey(),entry.getValue()[0]);
}
); Object o = JSONObject.toJSON(paramsMap);
result = o.toString();
}catch (Exception e){
logger.error("日志记录出错! getParameterMap == {}", e.getMessage());
} return result;
} /**
* 根据方法和传入的参数获取请求参数
*/
private String getParameter(Method method, Object[] args) {
List<Object> argList = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
if (requestBody != null) {
argList.add(args[i]);
}
RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
if (requestParam != null) {
Map<String, Object> map = new HashMap<>();
String key = parameters[i].getName();
if (!StringUtils.isEmpty(requestParam.value())) {
key = requestParam.value();
}
map.put(key, args[i]);
argList.add(map);
}else{
Map<String, Object> map = new HashMap<>();
String key = parameters[i].getName();
map.put(key, args[i]);
argList.add(map);
}
}
if (argList.size() == 0) {
return null;
} else if (argList.size() == 1) {
return argList.toString();
} else {
return argList.toString();
}
} }
AOP 面向切面 记录请求接口的日志的更多相关文章
- 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存
代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...
- Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存
本文梯子 本文3.0版本文章 代码已上传Github+Gitee,文末有地址 大神反馈: 零.今天完成的深红色部分 一.AOP 之 实现日志记录(服务层) 1.定义服务接口与实现类 2.在API层中添 ...
- 【原创】Android AOP面向切面编程AspectJ
一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...
- SpringBoot系列——aop 面向切面
前言 项目中我们经常会用到aop切面,比如日志记录:这里简单记录一下springboot是如何使用aop spring对aop的配置,来自springboot参考手册,Common applicati ...
- 极简SpringBoot指南-Chapter05-SpringBoot中的AOP面向切面编程简介
仓库地址 w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started ...
- [转] AOP面向切面编程
AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...
- Aspects– iOS的AOP面向切面编程的库
简介 一个简洁高效的用于使iOS支持AOP面向切面编程的库.它可以帮助你在不改变一个类或类实例的代码的前提下,有效更改类的行为.比iOS传统的 AOP方法,更加简单高效.支持在方法执行的前/后或替代原 ...
- Spring Boot2(六):使用Spring Boot整合AOP面向切面编程
一.前言 众所周知,spring最核心的两个功能是aop和ioc,即面向切面和控制反转.本文会讲一讲SpringBoot如何使用AOP实现面向切面的过程原理. 二.何为aop aop全称Aspec ...
- 浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
简介 我们都知道,Spring 框架作为后端主流框架之一,最有特点的三部分就是IOC控制反转.依赖注入.以及AOP切面.当然AOP作为一个Spring 的重要组成模块,当然IOC是不依赖于Spring ...
随机推荐
- [\u4e00-\u9fa5] //匹配中文字符
[\u4e00-\u9fa5] //匹配中文字符 ^[1-9]\d*$ //匹配正整数^[A-Za-z]+$ //匹配由26个英文字母组成的字符串^[A-Z]+$ //匹配由26 ...
- 福建工程学院寒假作业第一周G题
涨姿势题1 TimeLimit:1000MS MemoryLimit:128000KB 64-bit integer IO format:%lld 涨姿势题就是所谓的优化题,在组队赛中,队伍发现 ...
- OSCP考试回顾
技术背景 从2011年开始接触学习渗透测试,全职做过的职位有渗透测试,Python后端研发,DevOps,甲方安全打杂. 学习过程 首先考试报名,交钱.买了价值800美元的一个月lab,包含Pente ...
- 树莓派开发系列教程3--ssh、vnc远程访问
注意:树莓派系列的3篇文章里面的图片因为博客转移过程丢失了,非常抱歉 前言 远程访问有很多种方式可以实现.比如ssh.telnet.ftp.samba.远程桌面等等,各有优缺点.本文主要以ssh和远程 ...
- 【题解】BZOJ 3600: 没有人的算术——替罪羊树、线段树
题目传送门 题意 具体的自己去上面看吧...反正不是权限题. 简单来说,就是定义了一类新的数,每个数是0或者为 \((x_L, x_R)\) ,同时定义比较大小的方式为:非零数大于零,否则按字典序比较 ...
- Android 开发笔记(一) 按钮事件调用Activity
UI创建按钮及事件 Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);mEmailSignInB ...
- WebAPI使用Swagger生成接口文档
开发工具:VS2017 版本15.7.1 新建项目,选择空模板,下面只勾选WebAPI 配置Web.config <system.webServer> 节点改为 <system.we ...
- angular项目中使用ngSemantic
npm install ng-semantic --save npm install jquery --save 下载 Official Semantic UI bundle ( .zip ) fro ...
- 制作一棵ztree
我们在做web项目时,常会用到一些树形菜单.在此,我们利用ztree实现树形菜单的效果.zTree 是一个依靠 jQuery 实现的多功能 “树插件”.优异的性能.灵活的配置.多种功能的组合是 zTr ...
- php 学习try_catch测试抛出异常
/** * Class show * 一个catch接收抛出异常 */ class show { // 错误的演示 //try { //require ('test_try_catch.php'); ...