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 面向切面 记录请求接口的日志的更多相关文章

  1. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    代码已上传Github+Gitee,文末有地址 上回<从壹开始前后端分离[ .NET Core2.0 Api + Vue 2.0 + AOP + 分布式]框架之九 || 依赖注入IoC学习 + ...

  2. Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存

    本文梯子 本文3.0版本文章 代码已上传Github+Gitee,文末有地址 大神反馈: 零.今天完成的深红色部分 一.AOP 之 实现日志记录(服务层) 1.定义服务接口与实现类 2.在API层中添 ...

  3. 【原创】Android AOP面向切面编程AspectJ

    一.背景: 在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在视频模块,要想实现对用户对监控点的实时预览和远程回放行为进行统计, ...

  4. SpringBoot系列——aop 面向切面

    前言 项目中我们经常会用到aop切面,比如日志记录:这里简单记录一下springboot是如何使用aop spring对aop的配置,来自springboot参考手册,Common applicati ...

  5. 极简SpringBoot指南-Chapter05-SpringBoot中的AOP面向切面编程简介

    仓库地址 w4ngzhen/springboot-simple-guide: This is a project that guides SpringBoot users to get started ...

  6. [转] AOP面向切面编程

    AOP面向切面编程 AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术. ...

  7. Aspects– iOS的AOP面向切面编程的库

    简介 一个简洁高效的用于使iOS支持AOP面向切面编程的库.它可以帮助你在不改变一个类或类实例的代码的前提下,有效更改类的行为.比iOS传统的 AOP方法,更加简单高效.支持在方法执行的前/后或替代原 ...

  8. Spring Boot2(六):使用Spring Boot整合AOP面向切面编程

    一.前言 众所周知,spring最核心的两个功能是aop和ioc,即面向切面和控制反转.本文会讲一讲SpringBoot如何使用AOP实现面向切面的过程原理. 二.何为aop ​ aop全称Aspec ...

  9. 浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~

    简介 我们都知道,Spring 框架作为后端主流框架之一,最有特点的三部分就是IOC控制反转.依赖注入.以及AOP切面.当然AOP作为一个Spring 的重要组成模块,当然IOC是不依赖于Spring ...

随机推荐

  1. scrapy 爬虫踩过的坑(I)

    问题1:正则表达式没问题,但是爬虫进不了item方法 分析: 1. 可能是下载不到list 页面的内容.可以用 scrapy shell url 进行测试 2. 可能是allowed_domains ...

  2. password passphrase passcode 的区别

    In general, passphrases are long passwords and passcodes are numeric-only passwords.

  3. mac下 mysql / nginx 问题总汇

    "ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)&q ...

  4. Machine Learning系列--深入理解拉格朗日乘子法(Lagrange Multiplier) 和KKT条件

    在求取有约束条件的优化问题时,拉格朗日乘子法(Lagrange Multiplier) 和KKT条件是非常重要的两个求取方法,对于等式约束的优化问题,可以应用拉格朗日乘子法去求取最优值:如果含有不等式 ...

  5. oracle客户端不需要配置tnsnames.ora文件直接连接服务器数据库

    在以前的oracle使用过程中,想要在客户端连接到服务器时,都是在客户端中的tnsnames.ora文件配置如以下内容: adb = (DESCRIPTION = (ADDRESS_LIST = (A ...

  6. android 动态改变控件位置和大小 .

    动态改变控件位置的方法: setPadding()的方法更改布局位置. 如我要把Imageview下移200px:             ImageView.setPadding( ImageVie ...

  7. 《深入理解Java虚拟机》笔记--第三章 、垃圾收集器与内存分配策略

    1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言. Java的垃圾收集(Garbage Collection)主要关注堆和方法区的内存回收. 在GC堆进行回收前,第一件 ...

  8. ssh使两台机器建立连接

    ssh利用口令建立连接过程: 客户端--> 发送连接请求 --> 远程主机 --> 返回远程主机的公钥 --> 公钥加密客户端私钥+客户端公钥返回远程主机 --> 远程主 ...

  9. admin组件详解

    admin组件详解 先根据admin组件启动流程复习下django项目启动至请求过来发生的事 1将admin组件注册进app 2django项目启动 3在运行到定制的admin时执行其下面的apps文 ...

  10. (二) Mysql 数据类型简介

    第一节:整数类型.浮点数类型和定点数类型 1,整数类型 2,浮点数类型和定点数类型 M 表示:数据的总长度(不包括小数点): D 表示:小数位: 例如 decimal(5,2)      123.45 ...