springboot 啟動項目打印接口列表的實現
springboot 啟動項目打印接口列表
環境
- springboot 2.3.2.RELEASE
修改配置文件
logging: level: org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: trace
結果:
Springboot項目添加接口入參統一打印
需求:要求接口被調用時要打印被調用方法名,以及入參情況,參數格式化時選擇fastjson
註:使用fastjson序列化時脫敏,建議入參統一使用自定義的對象類型作為入參
如果不需要參數脫敏,直接使用增強中相關代碼,並去除參數脫敏相關代碼即可
新建註解,用於實現參數打印功能的增強
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ParamInfo { /** * 取消統一打印參數 * 默認為false統一打印 * 如需自定義參數打印 請賦值為true */ boolean unPrint() default false; /** * 需要脫敏的字段,如密碼等 */ String[] fields() default {}; }
自定義序列化規則
/** * 序列化過濾器:值替換 * */ public class ReplaceFieldFilter implements ValueFilter { /** * 需要進行替換的屬性名和替換值 * key:屬性名 * value:替換值 */ private Map<String, Object> fieldMap; public ReplaceFieldFilter() { } public ReplaceFieldFilter(Map<String, Object> fieldMap) { this.fieldMap = fieldMap; } @Override public Object process(Object o, String name, Object value) { if(!CollectionUtils.isEmpty(fieldMap)){ Iterator<Map.Entry<String, Object>> iterator = fieldMap.entrySet().iterator(); while (iterator.hasNext()){ Map.Entry<String, Object> next = iterator.next(); if(next.getKey().equalsIgnoreCase(name)){ return next.getValue(); } } } return value; } public Map<String, Object> getFieldMap() { return fieldMap; } public void setFieldMap(Map<String, Object> fieldMap) { this.fieldMap = fieldMap; } /** * 傳入需要脫敏的字段名,序列化時格式化為 * 號 */ public ReplaceFieldFilter(String... fields) { String str = "******"; fieldMap = new HashMap<>(4); for (String field : fields) { fieldMap.put(field, str); } } }
寫參數打印增強,這裡選擇環繞增強
@Component @Aspect //表示增強的執行順序,如果多個增強,數值小的先被執行 @Order(0) public class ParamInfoAspect { private static final Logger LOGGER = LoggerFactory.getLogger(ParamInfoAspect.class); @Around("execution(* com.service.impl.*.*(..))") public Object printParam(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); String requestId = RandomStringUtils.randomAlphanumeric(16); Object returnValue = null; try { Object[] args = joinPoint.getArgs(); // 獲取方法對象 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); //通過註解獲取脫敏字段,之後初始化fieldMap,完成字段脫敏 ParamInfo annotation = method.getAnnotation(ParamInfo.class); Map<String, Object> fieldMap = new HashMap<>(4); fieldMap.put("password", "******"); if (annotation != null) { //獲取需要脫敏的字段名數組 String[] fields = annotation.fields(); for (String field : fields) { fieldMap.put(field, "******"); } } String param; //參數整合,多字段入參整合為對象,單個對象入參格式不變 if (args.length > 1 || (args.length == 1 && args[0].getClass() == String.class)) { Map<String, Object> paramMap = new LinkedHashMap<>(); String[] parameterNames = signature.getParameterNames(); for (int i = 0; i < parameterNames.length; i++) { paramMap.put(parameterNames[i], args[i]); } param = "[" + JSON.toJSONString(paramMap, new ReplaceFieldFilter(fieldMap)) + "]"; } else { param = JSON.toJSONString(args, new ReplaceFieldFilter(fieldMap)); } String methodName = method.getName(); LOGGER.info("method:[{}], parameter:{}, requestId:[{}]", methodName, param, requestId); returnValue = joinPoint.proceed(); return returnValue; } catch (Exception e) { LOGGER.error("system is error:", e); //可在這裡定義程序異常時的錯誤返回值 returnValue = ErrorCode.SYSTEM_ERROR; return returnValue; } finally { LOGGER.info("request cost:{}ms, requestId:[{}]", System.currentTimeMillis() - startTime, requestId); LOGGER.info("returnValue:[{}], requestId:[{}]", returnValue, requestId); } } }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- java 通過反射遍歷所有字段修改值的實例代碼
- Java反射機制如何解決數據傳值為空的問題
- SpringBoot@Aspect 打印訪問請求和返回數據方式
- SpringAOP如何獲取方法參數上的註解
- 淺談基於SpringBoot實現一個簡單的權限控制註解