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 ...
随机推荐
- POJ 3734 Blocks (矩阵快速幂)
题目链接 Description Panda has received an assignment of painting a line of blocks. Since Panda is such ...
- RMQ之ST求区间最大值
题目链接:https://cn.vjudge.net/problem/HRBUST-1188 每一次按照二进制的方式进行更新,二维数组dp [i] [j],i表示下标,j表示从i 开始的往后移动2的j ...
- Fiddler 断点调试http请求
fiddler有两种断点,Before Requests(可以修改请求参数).After Responses(可以修改返回值) Before Requests 断点 1.设置Before Reques ...
- vista风格的cms企业html后台管理系统模板——后台
链接:http://pan.baidu.com/s/1c1Cv99e 密码:20yz
- NEO发行资产Token
NEO注册发行全局资产(Token 和 Share)功能已经在neo-gui里面集成,发行非常方便, 高级-注册资产 注册Token消耗GAS感人 4990 Gas 点击调用,获取交易ID为资产ID ...
- java反序列化漏洞
http://www.freebuf.com/vuls/86566.html 有时间了 仔细阅读
- USB描述符【整理】
USB描述符 USB描述符信息存储在USB设备中,在枚举过程中,USB主机会向USB设备发送GetDescriptor请求,USB设备在收到这个请求之后,会将USB描述符信息返回给USB主机,USB主 ...
- Tomcat: Connector中HTTP与AJP差别与整合
apache tomcat 整合(ajp proxy, http proxy) 1.软件: apache: httpd-2.2.17-win32-x86-openssl-0.9.8o.msi tomc ...
- 端口扫描———nmap
nmap教程之nmap命令使用示例(nmap使用方法) 浏览:8268 | 更新:2014-03-29 17:23 Nmap是一款网络扫描和主机检测的非常有用的工具.Nmap是不局限于仅仅收集信息和枚 ...
- 初窥Linux 之 最常用20条命令
玩过Linux的人都会知道,Linux中的命令的确是非常多,但是玩过Linux的人也从来不会因为Linux的命令如此之多而烦恼,因为我们只需要掌握我们最常用的命令就可以了.当然你也可以在使用时去找一下 ...