Java配置日志切面
作者:互联网
日志切面配置
什么是切面
面向切面编程(Aspect-oriented Programming 简称AOPAOP) ,是相对面向对象编程(Object-oriented Programming 简称OOP)的框架,作为OOP的一种功能补充. OOP主要的模块单元是类(class)。而AOP则是切面(aspect)。切面会将诸如事务管理这样跨越多个类型和对象的关注点模块化(在AOP的语义中,这类关注点被称为横切关注点(crosscutting))
切面能干什么
事务管理、权限控制、缓存控制、日志打印
使用切面的好处
- 集中处理某一关注点/横切逻辑
- 可以很方便的添加/删除关注点
- 侵入性少,增强代码可读性及可维护性 因此当想打印请求日志时很容* 易想到切面,对控制层代码0侵入
切面的使用(基于注解)
- @Aspect 声明该类为一个注解类
- @Pointcut 声明为切点
- @Before 在切点之前执行代码
- @After 在切点之后执行代码
- @AfterReturning 切点返回内容后执行代码,可以对切点的返回值进行封装
- @AfterThrowing 切点抛出异常后执行
- @Around 环绕,在切点前后执行代码
配置日志切面
1.指定切点
/**
*.AOP切入点表达式
*
*execution * com.bonc.service..*.*(..)
* execution 为切点修饰符
* 第一个位 为访问修饰符可以不写
* 第二个 * 方法返回值
* 第三位为 报名 *代表所有 ..代表所欲子包
* 第四位为 方法名
* 第五位 (..)代表任意参数
* 日志的入口一般为controller层所以切点为所有的controller
*
* */
@Pointcut("execution(* com.bonc..controller..*(..))")
public void logPointCut() {
}
2.构建访问日志实体
@Data
public class RequestInfo {
private String ip;
private String url;
private String httpMethod;
private String classMethod;
private Object requestParams;
private Object result;
private Long timeCost;
private RuntimeException exception;
}
3.用@Around注解 并且拼装参数
@Around("logPointCut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long start = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
RequestInfo requestInfo = new RequestInfo();
requestInfo.setIp(request.getRemoteAddr());
requestInfo.setUrl(request.getRequestURL().toString());
requestInfo.setHttpMethod(request.getMethod());
requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
proceedingJoinPoint.getSignature().getName()));
requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
Object result =null;
try {
result = proceedingJoinPoint.proceed();
}catch ( RuntimeException e){
requestInfo.setException(e);
}
requestInfo.setResult(result);
requestInfo.setTimeCost(System.currentTimeMillis() - start);
LOGGER.info("Request Info : {}", JSON.toJSONString(requestInfo));
return result;
}
/**
* 获取入参
* @param proceedingJoinPoint
*
* @return
* */
private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
//参数名
String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
//参数值
Object[] paramValues = proceedingJoinPoint.getArgs();
return buildRequestParam(paramNames, paramValues);
}
private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
Map<String, Object> requestParams = new HashMap<>();
for (int i = 0; i < paramNames.length; i++) {
Object value = paramValues[i];
if (! (value instanceof Serializable)) {
continue;
}
//如果是文件对象
if (value instanceof MultipartFile) {
MultipartFile file = (MultipartFile) value;
value = file.getOriginalFilename(); //获取文件名
}
requestParams.put(paramNames[i], value);
}
return requestParams;
}
## 4. 将该类声明为界面 并且将放置到spring容器中
```java
@Component
@Aspect
5.检查是否开启了自动注解
- 如果是springboot 项目可以跳过这一步
- 如果是使用sping mvc项目中需要在xml 检查是否有
<aop:aspectj-autoproxy/>
- 如果是使用JavaConfig 形式的项目中需要在主javaConfig文件中添加
@EnableAspectJAutoProxy 注解来开启切面
6. 启动项目,本地测试一下
正常结果如下
{
"classMethod": "*******",
"httpMethod": "GET",
"ip": "127.0.0.1",
"requestParams": {
"pageSize": 10,
"pageNum": 1
},
"result": {
"code": 200,
"msg": "成功"
},
"timeCost": 84,
"url": "http://127.0.0.1:8090/*****"
}
异常结果
{
"classMethod": "**",
"exception": {
"@type": "java.lang.ArithmeticException",
"localizedMessage": "/ by zero",
"message": "/ by zero",
"stackTrace": [
{
"className": "com.bonc.huanghai.api.controller.oa.AnnouncementController",
"fileName": "AnnouncementController.java",
"lineNumber": 70,
"methodName": "getAnnouncementList",
"nativeMethod": false
}
]
},
"httpMethod": "GET",
"ip": "127.0.0.1",
"requestParams": {
"pageSize": 10,
"pageNum": 1
},
"timeCost": 96,
"url": "http://127.0.0.1:8090/**"
}
代码
最后贴上所有的代码
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
* @author sz
* @version V1.0
* @Description: 请求切面(用一句话描述该文件做什么)
* @date 2021/2/6
*/
@Component
@Aspect
public class RequestLogAspect {
private final static Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class);
/**
*.AOP切入点表达式
*
*execution * com.bonc.service..*.*(..)
* execution 为切点修饰符
* 第一个位 为访问修饰符可以不写
* 第二个 * 方法返回值
* 第三位为 报名 *代表所有 ..代表所欲子包
* 第四位为 方法名
* 第五位 (..)代表任意参数
* 日志的入口一般为controller层所以切点为所有的controller
*
* */
@Pointcut("execution(* com.bonc..controller..*(..))")
public void logPointCut() {
}
@Around("logPointCut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long start = System.currentTimeMillis();
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
RequestInfo requestInfo = new RequestInfo();
requestInfo.setIp(request.getRemoteAddr());
requestInfo.setUrl(request.getRequestURL().toString());
requestInfo.setHttpMethod(request.getMethod());
requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
proceedingJoinPoint.getSignature().getName()));
requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
Object result =null;
try {
result = proceedingJoinPoint.proceed();
}catch ( RuntimeException e){
requestInfo.setException(e);
}
requestInfo.setResult(result);
requestInfo.setTimeCost(System.currentTimeMillis() - start);
LOGGER.info("Request Info : {}", JSON.toJSONString(requestInfo));
return result;
}
/**
* 获取入参
* @param proceedingJoinPoint
*
* @return
* */
private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
//参数名
String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
//参数值
Object[] paramValues = proceedingJoinPoint.getArgs();
return buildRequestParam(paramNames, paramValues);
}
private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
Map<String, Object> requestParams = new HashMap<>();
for (int i = 0; i < paramNames.length; i++) {
Object value = paramValues[i];
if (! (value instanceof Serializable)) {
continue;
}
//如果是文件对象
if (value instanceof MultipartFile) {
MultipartFile file = (MultipartFile) value;
value = file.getOriginalFilename(); //获取文件名
}
requestParams.put(paramNames[i], value);
}
return requestParams;
}
}
@Data
public class RequestInfo {
private String ip;
private String url;
private String httpMethod;
private String classMethod;
private Object requestParams;
private Object result;
private Long timeCost;
private RuntimeException exception;
}
标签:Object,Java,requestInfo,proceedingJoinPoint,private,切面,import,日志 来源: https://blog.csdn.net/qq_39684784/article/details/113725944