反射获取spring容器中业务模块类和方法的切面信息

阅读更多
@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 List baseClsList = 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 , Classclazz) {
        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>  buildParameterizedTypeImpleMap(Type type) throws ClassNotFoundException {
        Type[] typeArr = ((ParameterizedType)type).getActualTypeArguments();
        List> mList = new ArrayList<>();
        for(Type t : typeArr) {
            mList.add(getObjectFieldInfoMap("", t));
        }
        return mList;
    }

    /**
     * 获取一个对象类型的所有属性
     * @param cls
     * @return
     */
    private List getObjFields(Class cls){
        List list = new ArrayList<>();
        if(cls != Object.class) {
            Field[] fields = cls.getDeclaredFields();
            for(Field f : fields){
                list.add(f);
            }
            if (cls.getSuperclass() != null && cls.getSuperclass() != Object.class){
                list.addAll(getObjFields(cls.getSuperclass()));
            }
        }
        return list;
    }


    private boolean checkCanReflect(Type type) {
        if (type instanceof WildcardTypeImpl) {
            return false;
        } else if (type instanceof TypeVariableImpl) {
            return false;
        } else if (type.getClass() == Class.class) {
            Class cls = (Class) type;
            if (Throwable.class.isAssignableFrom(cls)) {
                return false;
            }
        }
        return true;
    }
}

 

类信息: 类名(含包名)

方法信息:

       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

 

根据 方法的请求内容和入参格式化信息,以及需要友好展示的属性,按照下面的逻辑完成数据定位和值的转换:


反射获取spring容器中业务模块类和方法的切面信息_第1张图片
 

 

 

 

如下是解析的代码:

/**
     * 根据数据格式描述符等获取友好展示的信息
     * @param mapDataInfo 原始数据
     * @param descInfo 数据的格式描述信息
     * @param sugarInfo 需要友好展示的属性格式信息
     * @param keyName 关键字名称(用于快速查询)
     * @param sb 友好展示的信息
     * @param sb_key 关键字结果
     */
    private void getMethodLogSugarInfo(Map mapDataInfo, 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;
    }

 

 

  • 反射获取spring容器中业务模块类和方法的切面信息_第2张图片
  • 大小: 35.3 KB
  • 查看图片附件

你可能感兴趣的:(反射获取spring容器中业务模块类和方法的切面信息)