react 前端框架如何驱动企业数字化转型与创新发展
596
2023-07-08
spring boot如何使用AOP统一处理web请求
为了保证服务的高可用,及时发现问题,迅速解决问题,为应用添加log是必不可少的。
但是随着项目的增大,方法增多,每个方法加单独加日志处理会有很多冗余
那在SpringBoot项目中如何统一的处理Web请求日志?
基本思想:
采用AOP的方式,拦截请求,写入日志
AOP 是面向切面的编程,就是在运行期通过动态代理的方式对代码进行增强处理
基于AOP不会破坏原来程序逻辑,因此它可以很好的对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
1.添加依赖
引入spring-boot-starter-web 依赖之后无需在引入相关的日志依赖,spring-boot-starter-web中已经集成了slf4j 的依赖
引入spring-boot-starter-aop 依赖之后,AOP 的功能即是启动状态
2.配置
application.properties添加
# AOP
spring.aop.auto=true
spring.aop.proxy-target-class=true
logback-spring.xml,主要是ControllerRequest那部分
3..实现
实现切面的注解
(1)类注解
A. @Aspect 将一个java类定义为切面类
B. @order(i) 标记切面类的处理优先级,i值越小,优先级别越高。可以注解类,也能注解到方法上
(2)方法注解
A. @Pointcut 定义一个切入点,可以是一个表达式
execution表达式,eg:
任意公共方法的执行
execution(public * *(..))
任何一个以“set”开始的方法的执行
execution(* set*(..))
定义在controller包里的任意方法的执行
execution(public * com.example.demo.controller.*(..))
定义在controller包里的任意方法的执行
execution(public * com.example.demo.controller.*.*(..))
定义在controller包和所有子包里的任意类的任意方法的执行
execution(public * com.example.demo.controller..*.*(..))
B. 实现在不同的位置切入
@Before 在切点前执行方法,内容为指定的切点
@After 在切点后,return前执行
@AfterReturning 切入点在 return内容之后(可用作处理返回值)
@Around 切入点在前后切入内容,并自己控制何时执行切入的内容
@AfterThrowing 处理当切入部分抛出异常后的逻辑
C.@order(i) 标记切点的优先级,i越小,优先级越高
@order(i)注解说明
注解类,i值是,值越小,优先级越高
注解方法,分两种情况
注解的是 @Before 是i值越小,优先级越高
注解的是 @After或@AfterReturning 中,i值越大,优先级越高
具体实现
package com.example.demo.configure;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
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-.InetAddress;
import java.util.HashMap;
import java.util.Map;
@Aspect
@Component
public class WebRequestLogAspect {
private final Logger loggerController = LoggerFactory.getLogger("ControllerRequest");
private final Logger logger = LoggerFactory.getLogger(WebRequestLogAspect.class);
ThreadLocal
ThreadLocal
ThreadLocal
ThreadLocal
ThreadLocal
ThreadLocal
ThreadLocal
private static Map
// 参数值
Object[] args = joinPoint.getArgs();
ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String[] parameterNames = pnd.getParameterNames(method);
Map
for (int i = 0; i < parameterNames.length; i++) {
paramMap.put(parameterNames[i], args[i] + "(" + args[i].getClass().getSimpleName() + ")");
}
return paramMap;
}
@Pointcut("execution(public * com.example.demo.controller..*.*(..))")
public void webRequestLog() {
}
/**
* 前置通知,方法调用前被调用
* @param joinPoint
*/
@Before("webRequestLog()")
public void doBefore(JoinPoint joinPoint) {
try {
startTime.set(System.currentTimeMillis());
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
beanName.set(joinPoint.getSignature().getDeclaringTypeName());
methodName.set(joinPoint.getSignature().getName());
uri.set(request.getRequestURI());
remoteAddr.set(getIpAddr(request));
user.set((String) request.getSession().getAttribute("user"));
} catch (Exception e) {
logger.error("***操作请求日志记录失败doBefore()***", e);
}
}
/**
* 环绕通知,环绕增强,相当于MethodInterceptor
* @param thisJoinPoint
*/
@Around("webRequestLog()")
public Object proceed(ProceedingJoinPoint thisJoinPoint) throws Throwable {
Object object = thisJoinPoint.proceed();
Map
params.set(fieldsName.toString());
return object;
}
/**
* 处理完请求返回内容
* @param result
*/
@AfterReturning(returning = "result", pointcut = "webRequestLog()")
public void doAfterReturning(Object result) {
try {
long requestTime = (System.currentTimeMillis() - startTime.get()) / 1000;
loggerController.info("请求耗时:" + requestTime + ", uri=" + uri.get() + "; beanName=" + beanName.get() + "; remoteAddr=" + remoteAddr.get() + "; user=" + user.get()
+ "; methodName=" + methodName.get() + "; params=" + params.get() + "; RESPONSE : " + result);
} catch (Exception e) {
logger.error("***操作请求日志记录失败doAfterReturning()***", e);
}
}
/**
* 获取登录用户远程主机ip地址
*
* @param request
* @return
*/
private String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) {
//根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (Exception e) {
e.printStackTrace();
}
ip = inet.getHostAddress();
}
}
// 多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ip != null && ip.length() > 15) {
if (ip.indexOf(",") > 0) {
ip = ip.substring(0, ip.indexOf(","));
}
}
return ip;
}
}
4.测试类
package com.example.demo.controller;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.dao.UserRepository;
import com.example.demo.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
public class Demo {
@RequestMapping (value = "test1")
public String test1(@RequestParam(defaultValue = "0") Integer id,@RequestParam(defaultValue = "0")String name){
return id+name;
}
@RequestMapping("hello")
public String hello() {
return "Hello World!";
}
@PostMapping("/updateStatus")
public Object updateStatus(@RequestBody JSONObject jsonParam) {
return jsonParam;
}
}
输出到logs/request/info.log内容
2019-09-11 13:31:45.729 [http-nio-8080-exec-4] INFO ControllerRequest - 请求耗时:0, uri=/test1; beanName=com.example.demo.controller.Demo; remoteAddr=172.27.0.17; user=null; methodName=test1; params={name=abcdef(String), id=123(Integer)}; RESPONSE : 123abcdef
2019-09-11 13:32:16.692 [http-nio-8080-exec-5] INFO ControllerRequest - 请求耗时:0, uri=/updateStatus; beanName=com.example.demo.controller.Demo; remoteAddr=172.27.0.17; user=null; methodName=updateStatus; params={jsonParam={"id":"17","type":3,"status":2}(JSONObject)}; RESPONSE : {"id":"17","type":3,"status":2}
2019-09-11 13:33:32.584 [http-nio-8080-exec-7] INFO ControllerRequest - 请求耗时:0, uri=/hello; beanName=com.example.demo.controller.Demo; remoteAddr=172.27.0.17; user=null; methodName=hello; params={}; RESPONSE : Hello World!
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~