首页
关于
留言
Search
1
红米 AX3000 (AX6) 路由器解锁 SSH 教程
6,676 阅读
2
网盘挂载程序sharelist美化教程
4,216 阅读
3
小米路由器 AX3600 开启SSH教程,官方固件即可安装 ShellClash开启科学上网
2,168 阅读
4
Oracle 甲骨文 ARM VPS 自动抢购脚本
1,819 阅读
5
编译带PassWall和SSR-plus插件的Openwrt系统
1,393 阅读
前端
Vue
React
后端
Java
Python
PHP
数据库
运维
杂谈
小程序
影视资源
登录
Search
标签搜索
Java
Linux
Mysql
IDEA
Debian
Docker
Springboot
CentOS
Cloudflare
Maven
JavaScript
SQL
Wordpress
宝塔
Nginx
Windows
MacBook
JS
CSS
Openwrt
William
累计撰写
144
篇文章
累计收到
702
条评论
首页
栏目
前端
Vue
React
后端
Java
Python
PHP
数据库
运维
杂谈
小程序
影视资源
页面
关于
留言
搜索到
2
篇与
的结果
2022-04-14
给 JAVA 项目添加 AOP 日志切面
切面介绍面向切面编程是一种编程范式,它作为OOP面向对象编程的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、权限控制、缓存控制、日志打印等等。AOP把软件的功能模块分为两个部分:核心关注点和横切关注点。业务处理的主要功能为核心关注点,而非核心、需要拓展的功能为横切关注点。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点进行分离,使用切面有以下好处:集中处理某一关注点/横切逻辑可以很方便的添加/删除关注点侵入性少,增强代码可读性及可维护性 因此当想打印请求日志时很容易想到切面,对控制层代码0侵入切面的使用【基于注解】@Aspect => 声明该类为一个注解类切点注解:@Pointcut => 定义一个切点,可以简化代码通知注解:@Before => 在切点之前执行代码@After => 在切点之后执行代码@AfterReturning => 切点返回内容后执行代码,可以对切点的返回值进行封装@AfterThrowing => 切点抛出异常后执行@Around => 环绕,在切点前后执行代码写一个请求日志切面使用 @Pointcut 定义切点@Pointcut("execution(* your_package.controller..*(..))") public void requestServer() { }@Pointcut定义了一个切点,因为是请求日志切边,因此切点定义的是Controller包下的所有类下的方法。定义切点以后在通知注解中直接使用requestServer方法名就可以了使用 @Before 再切点前执行@Before("requestServer()") public void doBefore(JoinPoint joinPoint) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); LOGGER.info("===============================Start========================"); LOGGER.info("IP : {}", request.getRemoteAddr()); LOGGER.info("URL : {}", request.getRequestURL().toString()); LOGGER.info("HTTP Method : {}", request.getMethod()); LOGGER.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); }在进入Controller方法前,打印出调用方IP、请求URL、HTTP请求类型、调用的方法名使用 @Around 打印进入控制层的入参@Around("requestServer()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long start = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); LOGGER.info("Request Params : {}", getRequestParams(proceedingJoinPoint)); LOGGER.info("Result : {}", result); LOGGER.info("Time Cost : {} ms", System.currentTimeMillis() - start); return result; }打印了入参、结果以及耗时getRquestParams 方法:private Map<String, Object> getRequestParams(ProceedingJoinPoint proceedingJoinPoint) { Map<String, Object> requestParams = new HashMap<>(); //参数名 String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames(); //参数值 Object[] paramValues = proceedingJoinPoint.getArgs(); for (int i = 0; i < paramNames.length; i++) { Object value = paramValues[i]; //如果是文件对象 if (value instanceof MultipartFile) { MultipartFile file = (MultipartFile) value; //获取文件名 value = file.getOriginalFilename(); } requestParams.put(paramNames[i], value); } return requestParams; }通过 @PathVariable 以及 @RequestParam 注解传递的参数无法打印出参数名,因此需要手动拼接一下参数名,同时对文件对象进行了特殊处理,只需获取文件名即可@After 方法调用后执行@After("requestServer()") public void doAfter(JoinPoint joinPoint) { LOGGER.info("=============================== End ========================"); }后置环绕这里就没有业务逻辑只是打印了End完整切面代码@Component @Aspect public class RequestLogAspect { private final static Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class); @Pointcut("execution(* your_package.controller..*(..))") public void requestServer() { } @Before("requestServer()") public void doBefore(JoinPoint joinPoint) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); LOGGER.info("===============================Start========================"); LOGGER.info("IP : {}", request.getRemoteAddr()); LOGGER.info("URL : {}", request.getRequestURL().toString()); LOGGER.info("HTTP Method : {}", request.getMethod()); LOGGER.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()); } @Around("requestServer()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long start = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); LOGGER.info("Request Params : {}", getRequestParams(proceedingJoinPoint)); LOGGER.info("Result : {}", result); LOGGER.info("Time Cost : {} ms", System.currentTimeMillis() - start); return result; } @After("requestServer()") public void doAfter(JoinPoint joinPoint) { LOGGER.info("=============================== End ========================"); } /** * 获取入参 * @param proceedingJoinPoint * * @return * */ private Map<String, Object> getRequestParams(ProceedingJoinPoint proceedingJoinPoint) { Map<String, Object> requestParams = new HashMap<>(); //参数名 String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames(); //参数值 Object[] paramValues = proceedingJoinPoint.getArgs(); for (int i = 0; i < paramNames.length; i++) { Object value = paramValues[i]; //如果是文件对象 if (value instanceof MultipartFile) { MultipartFile file = (MultipartFile) value; value = file.getOriginalFilename(); //获取文件名 } requestParams.put(paramNames[i], value); } return requestParams; } }但是,每个信息都打印一行,在高并发请求下确实会出现请求之间打印日志串行的问题,解决日志串行的问题只要将多行打印信息合并为一行就可以了,因此构造一个对象RequestInfo.java@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; }环绕通知方法体@Around("requestServer()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long start = System.currentTimeMillis(); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); Object result = proceedingJoinPoint.proceed(); 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)); requestInfo.setResult(result); requestInfo.setTimeCost(System.currentTimeMillis() - start); LOGGER.info("Request Info : {}", JSON.toJSONString(requestInfo)); return result; }将 url、http request 这些信息组装成 RequestInfo 对象,再序列化打印对象,打印序列化对象结果而不是直接打印对象是因为序列化有更直观、更清晰,同时可以借助在线解析工具对结果进行解析.就这样在解决高并发下请求串行问题的同时添加了对异常请求信息的打印,通过使用 @AfterThrowing 注解对抛出异常的方法进行处理RequestErrorInfo.java@Data public class RequestErrorInfo { private String ip; private String url; private String httpMethod; private String classMethod; private Object requestParams; private RuntimeException exception; }异常通知环绕体@AfterThrowing(pointcut = "requestServer()", throwing = "e") public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); RequestErrorInfo requestErrorInfo = new RequestErrorInfo(); requestErrorInfo.setIp(request.getRemoteAddr()); requestErrorInfo.setUrl(request.getRequestURL().toString()); requestErrorInfo.setHttpMethod(request.getMethod()); requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName())); requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint)); requestErrorInfo.setException(e); LOGGER.info("Error Request Info : {}", JSON.toJSONString(requestErrorInfo)); }为什么这里要单独搞一个异常环绕通知,因为都运行出问题了,统计运行时间也没什么意义了呀,所以这里不统计耗时,而是添加了异常的打印最后放一下优化后的完整日志请求切面代码:@Component @Aspect public class RequestLogAspect { private final static Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class); @Pointcut("execution(* your_package.controller..*(..))") public void requestServer() { } @Around("requestServer()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long start = System.currentTimeMillis(); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); Object result = proceedingJoinPoint.proceed(); 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)); requestInfo.setResult(result); requestInfo.setTimeCost(System.currentTimeMillis() - start); LOGGER.info("Request Info : {}", JSON.toJSONString(requestInfo)); return result; } @AfterThrowing(pointcut = "requestServer()", throwing = "e") public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); RequestErrorInfo requestErrorInfo = new RequestErrorInfo(); requestErrorInfo.setIp(request.getRemoteAddr()); requestErrorInfo.setUrl(request.getRequestURL().toString()); requestErrorInfo.setHttpMethod(request.getMethod()); requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName())); requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint)); requestErrorInfo.setException(e); LOGGER.info("Error Request Info : {}", JSON.toJSONString(requestErrorInfo)); } /** * 获取入参 * @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> getRequestParamsByJoinPoint(JoinPoint joinPoint) { //参数名 String[] paramNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames(); //参数值 Object[] paramValues = joinPoint.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 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; } @Data public class RequestErrorInfo { private String ip; private String url; private String httpMethod; private String classMethod; private Object requestParams; private RuntimeException exception; } }
2022年04月14日
19 阅读
0 评论
0 点赞
2020-07-30
使用fastjson进行Map与JSONObject以及List与JSONArray进行转换
1、Map转换成JSONObjectJSONObject object = JSONObject.parseObject(JSON.toJSONString(map));2、JSONObject转换成MapMap<string, object=""> itemMap = JSONObject.toJavaObject(itemJSONObject, Map.class);3、List转换成JSONArrayJSONArray array = JSONArray.parseArray(JSON.toJSONString(itemList));4、JSON转换成对象List<string> itemListString = JSON.parseArray(itemJsonString, String.class);
2020年07月30日
63 阅读
0 评论
0 点赞