@Service public class SpringContextHolder implements ApplicationContextAware { private static final Logger logger = LoggerFactory.getLogger(SpringContextHolder.class); private static ApplicationContext ctx; @Override public void setApplicationContext(ApplicationContext applicationContext) { SpringContextHolder.ctx = applicationContext; ctx.publishEvent(new BeanLoadedEvent(loadAllBeanClassInfo())); } private Class[] baseClassArr = new Class[]{ Date.class, String.class, Integer.class, int.class, Long.class, long.class, Double.class, double.class, Float.class, float.class, Enum.class, Boolean.class, boolean.class, Character.class, char.class, Object.class, Byte.class, byte.class }; private ListbaseClsList = Arrays.asList(baseClassArr); public static ApplicationContext getCtx(){ return SpringContextHolder.ctx; } public static Object getBean(String beanName) { return ctx.getBean(beanName); } public static T getBean(String beanName , Class clazz) { return ctx.getBean(beanName , clazz); } private Map > loadAllBeanClassInfo(){ Map > mapClassInfo = new HashMap<>(); try { Field f = ClassLoader.class.getDeclaredField("classes"); f.setAccessible(true); Vector classVector = (Vector) f.get(ClassLoader.getSystemClassLoader()); Vector vector = (Vector) classVector.clone(); logger.info("class num: {}", vector.size()); for (Object cv : vector) { String classInfo = cv.toString(); if (classInfo.startsWith("class com.xxx.xxx") && !classInfo.contains("domain") && !classInfo.contains("model") && !classInfo.contains("domain") && !classInfo.contains("SpringContextHolder")) { String className = classInfo.substring(classInfo.indexOf("com")); logger.info("******load class name: " + className); Class cls = Class.forName(className); Method[] methods = cls.getDeclaredMethods(); List mList = new ArrayList<>(); for (Method m : methods) { logger.info("\t******load method name: " + m.getName()); MeapClassInfo mInfo = new MeapClassInfo(); try { // 获取入参 Parameter[] params = m.getParameters(); Map mapParams = new HashMap<>(); for (Parameter p : params) { mapParams.putAll(getObjectFieldInfoMap(p.getName(), p.getType())); } // 获取返回内容 Map mapReturns = new HashMap<>(); Class retType = m.getReturnType(); Type retGerericType = m.getGenericReturnType(); mapReturns.putAll(getObjectFieldInfoMap("ret", retGerericType.getClass() == ParameterizedTypeImpl.class ? retGerericType : retType)); // 构造切面对象 mInfo.setCls(className); mInfo.setMethod(m.getName()); mInfo.setParams(mapParams); mInfo.setReturns(mapReturns); mList.add(mInfo); } catch (Exception ex) { logger.error("类【{}】的方法【{}】解析出现异常:{}", className, m.getName(), ex); } } mapClassInfo.put(className, mList); } } }catch (Throwable ex){ logger.error("反射获取类和方法信息时出现异常:{}", ex); } return mapClassInfo; } private Map getObjectFieldInfoMap(String fieldName, Type type) throws ClassNotFoundException { Map retMap = new HashMap<>(); // 一些特殊的类型不做处理 if(!checkCanReflect(type)){ return retMap; } // 基础类型直接返回type即可 if(baseClsList.contains(type)) { Map map = new HashMap<>(); map.put("tp", ((Class)type).getSimpleName()); retMap.put(fieldName, map); }else if(type instanceof ParameterizedTypeImpl){ Map map = new HashMap<>(); // 我们自定义的泛型(如:Response)在服务返回给客户端时,是Response的Map结构 // 需要特殊处理:先获取Response的结构,在将参数结构塞到Response的result属性中去 Type rawType = ((ParameterizedTypeImpl)type).getRawType(); if(rawType.getTypeName().equals(Response.class.getTypeName())){ map = getObjectFieldInfoMap("", rawType); ((Map )map.get("fd")).put("result", getObjectFieldInfoMap("", ((ParameterizedType) type).getActualTypeArguments()[0])); } else { // 如果是普通的list、map等参数化结构,直接获取类型表达式 // map.put("tp", type.getTypeName()); map.put("fd", buildParameterizedTypeImpleMap(type)); map.put("tp", ParameterizedTypeImpl.class.getSimpleName()); } //参数化属性在循环递归处理时都属于fieldName的子内容 //所以当fieldName为空时,表示不需要再分层次了,直接putAll if(StringUtils.isBlank(fieldName)){ retMap.putAll(map); }else { retMap.put(fieldName, map); } }else if(type.getClass() == Class.class){ // Field[] fields = ((Class)type).getDeclaredFields(); Field[] fields = getObjFields((Class)type).toArray(new Field[]{}); if(fields != null && fields.length > 0) { Map map = new HashMap<>(); for (Field f : fields) { try { Type tf = f.getGenericType(); if(checkCanReflect(tf)) { if (tf instanceof ParameterizedTypeImpl) { Map m = new HashMap<>(); // m.put("tp", tf.getTypeName()); // 屏蔽的原因是:在解析时无需关注具体是什么参数化的类型 m.put("tp", ParameterizedTypeImpl.class.getSimpleName()); m.put("fd", buildParameterizedTypeImpleMap(tf)); map.put(f.getName(), m); } else if (baseClsList.contains(f.getType())) { Map m = new HashMap<>(); m.put("tp", ((Class) tf).getSimpleName()); map.put(f.getName(), m); } else if (tf.getTypeName().startsWith("com.xxx.xxx")) { Map m = new HashMap<>(); m.put("tp", tf.getTypeName()); m.put("fd", getObjectFieldInfoMap(f.getName(), tf)); map.put(f.getName(), m); }else{ logger.info("暂不处理的类型: {}", tf.getTypeName()); } }else { logger.info("无法获取属性信息: {}", tf.getTypeName()); } }catch (Exception ex){ logger.info("获取GenericType失败:{}", ex); } } Map mAll = new HashMap<>(); mAll.put("tp", type.getTypeName()); mAll.put("fd", map); if(StringUtils.isBlank(fieldName)){ retMap.putAll(mAll); }else { retMap.put(fieldName, mAll); } }else{ Map m = new HashMap<>(); m.put("tp", type.getTypeName()); m.put("fd", new HashMap<>()); retMap.put(fieldName, m); } } return retMap; } private List
类信息: 类名(含包名)
方法信息:
1)方法名
2)入参信息(基本类型参数、参数化参数、普通对象参数(含基类),以及类型的嵌套)
3)返回对象信息(结构同入参信息)
4)一般泛型的原型(rawType)是我们不太关注的,但是我们自定义的一些泛型类(如:Response类),原型属性也是需要的,针对这种情况,需要把 泛型类作为一个属性塞到原型类的某个属性中(Response中,是将泛型塞到 result字段中去的)
5)类型嵌套需要递归处理
方法参数和返回信息的构造规则:
1)采用 json,结构大致为: {"arg0": {"tp": "", "fd": {}}}
如果是基本格式,那么解析为: {"arg0": {"tp": "Long"}}
如果是普通对象,那么解析为: {"arg0": {"tp": "", "fd": {“xxx": {"tp": "Long"}}}},无限嵌套
如果是参数化结构,那么解析为:
{"arg0": {"tp": "", "fd": [{"tp": "Long"}, {"tp": "String"}]}}
{"arg0": {"tp": "", "fd": [{"tp": "com.xxx.xxx.....", "fd": {"xxx": {}, "xxx": {}}}]}}
2)反射获取方法的几个参数时,无法得到参数名,只有 arg0 arg1 这样的
3)反射获取方法的返回时,没有变量名,手动赋值:ret
4)方法入参示例:
{ "arg0": { "tp": "xxx.proxy.service.entity.req.TemplateDeletingReq", "fd": { "channelNo": { "tp": "String" }, "templateId": { "tp": "String" }, "productNo": { "tp": "String" } } } }
5)方法返回示例:
{ "ret": { "tp": "xxx.proxy.service.entity.resp.BaseResp", "fd": { "retCode": { "tp": "String" }, "retMsg": { "tp": "String" } } } }
6)一个比较复杂的示例:
{ "ret": { "tp": "xxx.service.domain.resp.Response", "fd": { "retDesc": { "tp": "String" }, "result": { "tp": "xxx.service.domain.model.mesp.EmployeeBusiness", "fd": { "groupBusinessId": { "tp": "Long" }, "groupId": { "tp": "Long" }, "orderList": { "tp": "ParameterizedTypeImpl", "fd": [ { "tp": "xxx.service.domain.model.mesp.EmployeeBusinessOrder", "fd": { "businessRetDesc": { "tp": "String" }, "businessContentType": { "tp": "Long" }, "phoneNo": { "tp": "String" }, "setStatus": { "tp": "Long" }, "setContentId": { "tp": "Long" }, "operatorUserId": { "tp": "Long" } } } ] }, "confirmTime": { "tp": "Date" } } }, "retCode": { "tp": "String" } } } }
前面说过方法的入参和出参的结构都是以层次化的json数据进行描述的,
为了方便的获取需要友好展示的数据,那么语法糖中也是层次化的,如下所示:
{ "arg0": { "tp": "xxx.proxy.service.entity.req.TemplateDeletingReq", "fd": { "channelNo": { "tp": "String" }, "templateId": { "tp": "String" }, "productNo": { "tp": "String" } } } }
我要记录channelNo的话,那么语法糖中的属性名格式为: arg0.chennelNo
而且不考虑channelNo是在arg0的普通字段里面,还是在参数化的字段里面
转换规则为 {"ka": "渠道编号", "va": ""}
完整的配置为: {"arg0.channleNo": {"ka": "渠道编号", "va": ""}}
其中:
ka 是 keyAlias
va 是 valueAlias
根据 方法的请求内容和入参格式化信息,以及需要友好展示的属性,按照下面的逻辑完成数据定位和值的转换:
如下是解析的代码:
/** * 根据数据格式描述符等获取友好展示的信息 * @param mapDataInfo 原始数据 * @param descInfo 数据的格式描述信息 * @param sugarInfo 需要友好展示的属性格式信息 * @param keyName 关键字名称(用于快速查询) * @param sb 友好展示的信息 * @param sb_key 关键字结果 */ private void getMethodLogSugarInfo(MapmapDataInfo, String descInfo, String sugarInfo, String keyName, StringBuilder sb, StringBuilder sb_key){ Map mapDescInfo = new HashMap<>(); //数据格式 if(StringUtils.isNotBlank(descInfo)){ mapDescInfo = JsonUtil.decode2MapObject(descInfo); } Map > mapSugarInfo = new HashMap<>(); //需要友好展示的数据格式 if(StringUtils.isNotBlank(sugarInfo)){ mapSugarInfo = JsonUtil.decode(sugarInfo.getBytes(), new TypeReference >>(){}, "utf-8"); } // 开始获取友好的展示信息 if(mapSugarInfo.size() > 0) { sb.append("关键信息如下:\n"); } for(String field : mapSugarInfo.keySet()) { Map tmpDescInfo = new HashMap<>(); Map tmpDataInfo = new HashMap<>(); tmpDescInfo.putAll(mapDescInfo); tmpDataInfo.putAll(mapDataInfo); getObjectSugarInfo(tmpDescInfo, tmpDataInfo, field, mapSugarInfo.get(field), sb, sb_key, field.equals(keyName), ""); } } /** * 根据字段sugarField的命名规则从数据副本中获取字段值并进行转义 * @param objDesc 对象格式描述副本 * @param objData 数据对象副本 * @param sugarField 需要转义后展示在界面的字段 * @param alias 转义规则 * @param segment 如果该字段不为空,说明是参数化解析的递归 * @return */ private void getObjectSugarInfo(Map objDesc, Map objData, String sugarField, Map alias, StringBuilder sb, StringBuilder sb_key, boolean isKeyField, String segment){ try { String seg0; if(StringUtils.isBlank(segment)) { String[] segments = sugarField.split("\\."); seg0 = segments[0]; objDesc = (Map ) objDesc.get(seg0); }else{ seg0 = segment; } String tp = objDesc.get("tp").toString(); if (baseClsList.contains(tp)) { sb.append(String.format("[%s]- [%s]\n", alias.get("ka"), objData.get(seg0))); if(isKeyField){ sb_key.append(String.format("%s,", objData.get(seg0))); } } else { if(tp.equals(ParameterizedTypeStr)){ //参数化 String first = objData.get(seg0).toString().substring(0, 1); if(first.equals("{")){ //如果是map,直接写进结果中 String v = JsonUtil.encodeString(objData.get(seg0)); sb.append(String.format("[%s]- [%s]\n", alias.get("ka"), v)); if(isKeyField){ sb_key.append(String.format("%s,", v)); } }else if(first.equals("[")){ //如果是列表,则循环获取 List > objDataList = (List >)objData.get(seg0); List > objDescList = (List >)objDesc.get("fd"); for(Map m : objDataList){ getObjectSugarInfo(objDescList.get(0), m, sugarField, alias, sb, sb_key, isKeyField, seg0); } }else{ logger.info("不支持的解析类型,直接拼接字段值。"); String v = JsonUtil.encodeString(objData.get(seg0)); sb.append(String.format("[%s]- [%s]\n", v)); if(isKeyField){ sb_key.append(String.format("%s,", v)); } } } else { //非参数化 objDesc = (Map ) objDesc.get("fd"); // segment不为空的时候,是参数化属性递归获取下一层的内容,但是objData无需进行层次深入 if(StringUtils.isBlank(segment)){ objData = (Map ) objData.get(seg0); } getObjectSugarInfo(objDesc, objData, sugarField.substring(seg0.length() + 1), alias, sb, sb_key, isKeyField, ""); } } }catch (Exception ex){ logger.error("获取友好转义的信息出现异常:", ex); } } /** * 根据规则转换属性值 * @param value 要进行转换的属性值 * @param regex 转换规则 * @return */ private String transferValue(String value, String regex){ return value; }