当前位置: 首页 > news >正文

代码优化~关注核心逻辑,封装常用规则

业务背景

活用函数式编程,可减少很多代码,提高开发效率
函数式编程简单入门,很早写的,比较简单,感兴趣可以看看
函数式编程介绍跳转
具体会以实战中的每个case带着大家去体会这种函数式编程的便捷。


api接口通用逻辑 ~ 供给型函数式接口案例 supplier

`api请求通用操作

  1. 打印日志
  2. 请求底层方法
    因此可抽象成

package com.dgut.edu.cn.springbootxiaozheng.controller;

import com.dgut.edu.cn.springbootxiaozheng.function.ExSupplier;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;


/**
 * @description:
 * @author: hongbin.zheng
 * @create: 2022-09-14 18:05
 **/
@Slf4j
public class OperateTemplateUtil {
	public static <T> T operate(ExSupplier<T ,Exception> operate, Logger logger,String eventPrefix, String method,long logSlowRespMs, Object... reqParam) throws Exception {
		long beginTime = System.currentTimeMillis();
		Exception ex = null;
		try {
			return operate.get();
		} catch (Exception e) {
			ex = e;
			if (e instanceof Exception) {
				throw e;
			}
			throw new RuntimeException("错误信息");
		} finally {
			// 一个请求记录一条响应日志,查询请求不记录响应内容
			// TODO 通用打印日志防范
			logReadReq(logger, beginTime, eventPrefix, method, ex,
					logSlowRespMs, reqParam);
		}
	}

	/**
	 * 记录OSP读请求的日志
	 * @param logger VenusLogger实例
	 * @param beginTime 请求开始处理时间(System.currentTimeMillis())
	 * @param eventPrefix 事件名的业务前缀(可能是业务模块相关的前缀,比如寻仓管理:“locate.admin.”)
	 * @param method osp方法名,将添加到eventName和message,方便按方法统计和检索
	 * @param ex 请求处理过程中抛出的异常,可能为null
	 * @param logSlowRespMs 慢响应阈值,单位毫秒,通常是配置值,比如500ms
	 * @param reqParam 请求的入参
	 */
	public static void logReadReq(Logger logger, long beginTime, String eventPrefix, String method, Exception ex,
			long logSlowRespMs, Object... reqParam) {
		long cost = System.currentTimeMillis() - beginTime;

		if (ex != null) {
			// 出现非osp异常,打印ERROR日志
			logger.error("********");
			return;
		}
		if (logger.isDebugEnabled()) {
			// 读请求,出入参情况打印到debug日志
			logger.info("********");
			return;
		}
	}
}

应用层

	private String EVENT_NAME_PREFIX = "CallOtherInterfaceServiceImpl";

	protected  <T> T operate(ExSupplier<T, Exception> operate, String method, Object... req) throws Exception {
		return OperateTemplateUtil.operate(operate, getLogger(), EVENT_NAME_PREFIX, method,
				6000L, req);
	}

	private Logger getLogger() {
		return LoggerFactory.getLogger("call");
	}
	
	public String getAppealCount(String req) throws Exception {
		return operate(() -> call(req), "queryAppealCount");
	}

接口重试代码 ~ 供给型函数式接口案例 supplier

说明:在请求外部接口的时候,常常会加上重试的逻辑。如果现在对接N个接口,就要写N份重试的代码逻辑,那能否只关注业务逻辑,不关心重试的逻辑呢

正常重试的代码

	public String callWithRetry(String userName, Integer retryTime) {
		String eventName = "CallOtherInterfaceServiceImpl_call";
		String result = "";
		do {
			
			try {
				// TODO 调用外部接口
				result = "调用外部接口, 获取用户信息成功,当前用户信息:aa";
				// 如果调用成功,没抛出异常,则结束
				break;
			} catch (Exception e) {
			    // 如果重试次数大于0,则要重试
				if (retryTime > 0) {
					log.info(eventName, userName, "重试中......");
					retryTime--;
				} else {
					log.info(eventName, userName, "重试后依旧失败,抛出异常......");
					throw e;
				}
			}
		} while (retryTime > 0);
		System.out.println("============");
		System.out.println(result);
		System.out.println("============");
		return result;
	}

controller代码

@RequestMapping("/callWithRetry")
	@ResponseBody
	public String callWithRetry() {
		String username = "xiaozheng";
		return callOtherInterfaceService.callWithRetry(username, 3);
	}

问题:每个接口的实现类都需要完成自己的重试逻辑,写起来就会很冗余 \color{red}问题:每个接口的实现类都需要完成自己的重试逻辑,写起来就会很冗余 问题:每个接口的实现类都需要完成自己的重试逻辑,写起来就会很冗余

代码优化,把每个接口的调用看成是一个提供型函数,这样就能统一

函数式方法定义

package com.dgut.edu.cn.springbootxiaozheng.function;

/**
 * hongbin.zheng
 * @param <T>
 * @param <E>
 */
@FunctionalInterface
public interface ExSupplier<T, E extends Throwable> {

	/**
	 *
	 * @return
	 * @throws E
	 */
	T get() throws E;

}

重试工具类

package com.dgut.edu.cn.springbootxiaozheng.utils;

import com.dgut.edu.cn.springbootxiaozheng.function.ExSupplier;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.Objects;

/**
 * @description:
 * @author: hongbin.zheng
 * @create: 2022-09-14 14:23
 **/
@Slf4j
public class RetryUtils {
	private static final int defaultRetryTimes = 2;

	public static <T, R, E extends Throwable> T doWithRetry(ExSupplier<T, E> supplier, String eventName, R logObj)
			throws E {
		return doWithRetry(supplier, defaultRetryTimes, eventName, logObj);
	}

	public static <T, R, E extends Throwable> T doWithRetry(ExSupplier<T, E> supplier, int times, String eventName,
			R logObj) throws E {
		try {
			return supplier.get();
		} catch (Throwable e) {
			if (times > 0) {
				log.warn(eventName, getInfoMap(logObj, times, eventName, e), "正在重试中");
				Thread.yield(); // 让当前线程的优先级降低,减少可能存在的细微竞争
				return doWithRetry(supplier, --times, eventName, logObj);
			} else {
				log.error(eventName, getInfoMap(logObj, times, eventName, e),"重试失败");
				throw e;
			}
		}
	}


	private static Map<String, Object> getInfoMap(Object logObj, int times, String eventName, Throwable e) {
		Map<String, Object> infoMap = Maps.newHashMap();
		infoMap.put("logObj", logObj);
		infoMap.put("retryTimes", times);
		infoMap.put("eventName", eventName);
		infoMap.put("msg", e.getMessage());
		return infoMap;
	}
}

工具类使用

public String call(String userName) {
		String eventName = "CallOtherInterfaceServiceImpl_call";
		String s = RetryUtils.doWithRetry(() -> "获取用户信息成功,当前用户信息:aa", eventName, userName);
		System.out.println("============");
		System.out.println(s);
		System.out.println("============");
		return s;
	}

controller 测试

@RequestMapping("/call")
	@ResponseBody
	public String call() {
		String username = "xiaozheng";
		return callOtherInterfaceService.call(username);
	}

这样就完成了
在这里插入图片描述

批量插入 兜底 单个插入 ~ consumer 消费型函数式接口 案例

消费型函数式接口定义

package com.dgut.edu.cn.springbootxiaozheng.function;

import java.util.Objects;

/**
 * @see java.util.function.Consumer
 */
@FunctionalInterface
public interface ExConsumer<T> {

	void accept(T t) throws Exception;

	default ExConsumer<T> andThen(ExConsumer<? super T> after) {
		Objects.requireNonNull(after);
		return (T t) -> {
			accept(t);
			after.accept(t);
		};
	}
}

工具类

/**
	 * 批量处理降级为单条处理
	 * @param param
	 * @param eventName
	 * @param batchProcess
	 * @param singleProcess
	 * @param <T>
	 * @return 处理成功数据
	 */
	public static <T> List<T> demoteProcessTry(List<T> param,String eventName, ExConsumer<List<T>> batchProcess, ExConsumer<T> singleProcess){
		//处理成功的对象
		List<T> successEntityList ;
		try {
			batchProcess.accept(param);
			successEntityList = param;
		}catch (Exception e){
			log.error(eventName,param,"批量处理失败,改为单条处理");
			successEntityList= Lists.newArrayListWithCapacity(param.size());
			for (T t : param) {
				try {
					singleProcess.accept(t);
					successEntityList.add(t);
				}catch (Exception ex){
					log.error(eventName,param,"单条处理失败");
				}
			}
		}
		return successEntityList;
	}

mapper类

package com.dgut.edu.cn.springbootxiaozheng.mapper;

import com.dgut.edu.cn.springbootxiaozheng.domain.UserVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @description:
 * @author: hongbin.zheng
 * @create: 2022-09-14 17:34
 **/
@Mapper
public interface UserVoMapper {
	int insert(UserVo userVo);

	int batchInsert(@Param("list") List<UserVo> userVoList);


}

测试类

@Autowired
	UserVoMapper userVoMapper;
	@RequestMapping("insert")
	@ResponseBody
	public String insert() {
		List<UserVo> userVoList = UserVo.getUserVoList();
		String eventName = "insert";
		RetryUtils.demoteProcessTry(userVoList, eventName, list -> userVoMapper.batchInsert(list),
						vo -> userVoMapper.insert(vo));
		return  "success";

	}

总结

知道的越多,不知道的越多,希望对你有帮助! \color{red}知道的越多,不知道的越多,希望对你有帮助! 知道的越多,不知道的越多,希望对你有帮助!

相关文章:

  • Android车载开发基础学习——蓝牙通信是如何实现的?
  • 智慧政务、数字化优先与数字机器人,政务领域正在开启“政务新视界”
  • DES算法是对称算法吗,能否通过在线工具进行DES解密?
  • 【车间调度】基于GA/PSO/SA/ACO/TS优化算法的车间调度比较(Matlab代码实现)
  • springBoot 的默认线程池-ThreadPoolTaskExecutor
  • 暑期结束为你的新学期立下Flag吧
  • 大数字符串加法
  • ROS1云课→28机器人代价地图配置
  • 设计新鲜事(News Feed)系统
  • 【气动学】基于matlab GUI弹道问题(含初始角度、速度、空气阻力、水平风)【含Matlab源码 2117期】
  • 力扣leetcode 1619. 删除某些元素后的数组均值
  • 嵌入式入门-交叉编译、bootloader、kernel、根文件系统关系
  • Google Earth Engine(GEE)——快速建立一个10km的格网
  • 关于微信学习的网站
  • 微服务项目:尚融宝(38)(核心业务流程:申请借款额度(1))
  • 自己简单写的 事件订阅机制
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • CentOS 7 防火墙操作
  • centos安装java运行环境jdk+tomcat
  • js对象的深浅拷贝
  • MySQL用户中的%到底包不包括localhost?
  • react 代码优化(一) ——事件处理
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • Vue小说阅读器(仿追书神器)
  • Windows Containers 大冒险: 容器网络
  • 对象管理器(defineProperty)学习笔记
  • 给新手的新浪微博 SDK 集成教程【一】
  • 基于遗传算法的优化问题求解
  • 计算机常识 - 收藏集 - 掘金
  • 记一次用 NodeJs 实现模拟登录的思路
  • 解析带emoji和链接的聊天系统消息
  • 今年的LC3大会没了?
  • 我们雇佣了一只大猴子...
  • ### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException
  • #数学建模# 线性规划问题的Matlab求解
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (1)(1.9) MSP (version 4.2)
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (转) ns2/nam与nam实现相关的文件
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (转载)hibernate缓存
  • .NET Framework 的 bug?try-catch-when 中如果 when 语句抛出异常,程序将彻底崩溃
  • .Net mvc总结
  • .NET 事件模型教程(二)
  • .net 验证控件和javaScript的冲突问题
  • .NetCore实践篇:分布式监控Zipkin持久化之殇
  • .net开源工作流引擎ccflow表单数据返回值Pop分组模式和表格模式对比
  • .sdf和.msp文件读取
  • @SpringBootApplication 包含的三个注解及其含义
  • @value 静态变量_Python彻底搞懂:变量、对象、赋值、引用、拷贝