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 ...
随机推荐
- Vue的keep-alive
Vue的keep-alive: 简答的做下理解 缓存!页面从某一个页面跳转到另一个页面的时候,需要进行一定的缓存,然后这个时候调用的钩子函数是actived,而在第一次加载的时候,created.ac ...
- VMware 克隆多台Linux机器并配置IP的方法
我们首先要知道 VMware 三种网络模式的区别. ①.Bridged(桥接模式):就是将主机网卡与虚拟机虚拟的网卡利用虚拟网桥进行通信.在桥接的作用下,类似于把物理主机虚拟为一个交换机,所有桥接设置 ...
- Spring 控制台运行及RestTemplate实现Eurka负载均衡
spring使用控制台运行方式 spring.main.web-application-type=none新老版本的配置有点差异 Maven的modules只是实现了一个顺序编译,一次多个项目一起生成 ...
- C#子线程中更新ui
本文实例总结了C#子线程更新UI控件的方法,对于桌面应用程序设计的UI界面控制来说非常有实用价值.分享给大家供大家参考之用.具体分析如下: 一般在winform C/S程序中经常会在子线程中更新控件的 ...
- 转:在CentOS 7.3使用yum安装 MySql5.6.24
按照CentOS 6.5的方法在CentOS 7上安装会失败,需要按照下文进行安装. 1.安装rpm包 Linux系统自带的repo是不会自动更新每个软件的最新版本(基本都是比较靠后的稳定版),所以无 ...
- leetcode 之Sum系列(七)
第一题是Two Sum 同样是用哈希表来做,需要注意的是在查打gap是要排除本身.比如target为4,有一个值为2,gap同样为2. vector<int> twoSum(vector& ...
- 20165301陈潭飞2017-2018-2 20165301 实验三《Java面向对象程序设计》实验报告
2017-2018-2 20165301 实验三<Java面向对象程序设计>实验报告 一.敏捷开发与XP实践-1 实验要求: 在IDEA中使用工具(Code->Reformate C ...
- Valid Parentheses——栈经典
Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the inpu ...
- 强大到无与伦比的Graphviz
图1 hello world 尝试画复杂一些的图: 一直苦苦寻找用于图论的画图软件,偶然在Matrix67的这篇博文里找到. Graphviz使用dot语言,这门不仅语言非常简单易学,而且功能却非常强 ...
- 使用php扩展mcrypt实现AES加密
AES(Advanced Encryption Standard,高级加密标准)是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用.Rijndael是 ...