`

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

    博客分类:
  • java
 
阅读更多
@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<Class> baseClsList = Arrays.asList(baseClassArr);

    public static ApplicationContext getCtx(){
        return SpringContextHolder.ctx;
    }

    public static Object getBean(String beanName) {
        return ctx.getBean(beanName);
    }

    public static <T> T getBean(String beanName , Class<T>clazz) {
        return ctx.getBean(beanName , clazz);
    }

    private Map<String, List<MeapClassInfo>> loadAllBeanClassInfo(){
        Map<String, List<MeapClassInfo>> 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<MeapClassInfo> 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<String, Object> mapParams = new HashMap<>();
                            for (Parameter p : params) {
                                mapParams.putAll(getObjectFieldInfoMap(p.getName(), p.getType()));
                            }
                            // 获取返回内容
                            Map<String, Object> 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<String, Object> getObjectFieldInfoMap(String fieldName, Type type) throws ClassNotFoundException {
        Map<String, Object> retMap = new HashMap<>();
        // 一些特殊的类型不做处理
        if(!checkCanReflect(type)){
            return retMap;
        }
        // 基础类型直接返回type即可
        if(baseClsList.contains(type)) {
            Map<String, Object> map = new HashMap<>();
            map.put("tp", ((Class)type).getSimpleName());
            retMap.put(fieldName, map);
        }else if(type instanceof ParameterizedTypeImpl){
            Map<String, Object> 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<String, Object>)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<String, Object> map = new HashMap<>();
                for (Field f : fields) {
                    try {
                        Type tf = f.getGenericType();
                        if(checkCanReflect(tf)) {
                            if (tf instanceof ParameterizedTypeImpl) {
                                Map<String, Object> 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<String, Object> m = new HashMap<>();
                                m.put("tp", ((Class) tf).getSimpleName());
                                map.put(f.getName(), m);
                            } else if (tf.getTypeName().startsWith("com.xxx.xxx")) {
                                Map<String, Object> 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<String, Object> 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<String, Object> m = new HashMap<>();
                m.put("tp", type.getTypeName());
                m.put("fd", new HashMap<>());
                retMap.put(fieldName, m);
            }
        }
        return retMap;
    }

    private List<Map<String, Object>>  buildParameterizedTypeImpleMap(Type type) throws ClassNotFoundException {
        Type[] typeArr = ((ParameterizedType)type).getActualTypeArguments();
        List<Map<String, Object>> mList = new ArrayList<>();
        for(Type t : typeArr) {
            mList.add(getObjectFieldInfoMap("", t));
        }
        return mList;
    }

    /**
     * 获取一个对象类型的所有属性
     * @param cls
     * @return
     */
    private List<Field> getObjFields(Class cls){
        List<Field> 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

 

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



 

 

 

 

如下是解析的代码:

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

        Map<String, Object> mapDescInfo = new HashMap<>(); //数据格式
        if(StringUtils.isNotBlank(descInfo)){
            mapDescInfo = JsonUtil.decode2MapObject(descInfo);
        }

        Map<String, Map<String, String>> mapSugarInfo = new HashMap<>(); //需要友好展示的数据格式
        if(StringUtils.isNotBlank(sugarInfo)){
            mapSugarInfo = JsonUtil.decode(sugarInfo.getBytes(),
                    new TypeReference<Map<String, Map<String, String>>>(){}, "utf-8");
        }

        // 开始获取友好的展示信息
        if(mapSugarInfo.size() > 0) {
            sb.append("关键信息如下:\n");
        }
        for(String field : mapSugarInfo.keySet()) {
            Map<String, Object> tmpDescInfo = new HashMap<>();
            Map<String, Object> 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<String, Object> objDesc, Map<String, Object> objData,
                                             String sugarField, Map<String, String> 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<String, Object>) 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<Map<String, Object>> objDataList = (List<Map<String, Object>>)objData.get(seg0);
                        List<Map<String, Object>> objDescList = (List<Map<String, Object>>)objDesc.get("fd");
                        for(Map<String, Object> 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<String, Object>) objDesc.get("fd");
                    // segment不为空的时候,是参数化属性递归获取下一层的内容,但是objData无需进行层次深入
                    if(StringUtils.isBlank(segment)){
                        objData = (Map<String, Object>) 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;
    }

 

 

  • 大小: 35.3 KB
分享到:
评论

相关推荐

    Spring容器的通俗理解及简单写法

    在Java开发中,Spring容器(也称为ApplicationContext或BeanFactory)扮演着重要角色,它通过控制反转(Inversion of Control, IOC)和依赖注入(Dependency Injection, DI)的概念,帮助开发者构建松散耦合的系统。...

    Spring全家桶知识笔记.pdf

    Spring框架的核心特性包括轻量级、控制反转(IoC)、面向切面编程(AOP)、容器化管理和框架集合,使其在与其他框架的整合上非常灵活。 在Spring中,bean的注入与装配是核心概念之一,有多种方式可以实现,例如XML...

    模仿实现spring经典绝对值得看

    1. **IoC(控制反转)**:IoC是Spring的核心特性,它将对象的创建和管理从代码中分离出来,由Spring容器负责。当使用反射时,Spring可以根据XML或Java配置文件中的定义,动态地创建和装配Bean。 2. **Bean的生命...

    jdk6.0从入门到精通-----chapter16反射机制-spring AOP

    反射是Java中的一种强大工具,允许程序在运行时检查类、接口、字段和方法的信息,甚至可以动态创建对象和调用方法。Spring AOP则是一种设计模式,用于分离关注点,通过代理模式实现横切关注点,如日志、事务管理等。...

    Spring简介和入门

    反射允许程序在运行时动态获取类的信息并调用其方法。例如,`java.lang.Class`类代表了Java类的信息,可以使用`Class.forName()`加载指定类,`Class.getMethod()`找到类的方法,然后通过`Method.invoke()`来执行这个...

    springioc和spring aop

    Spring框架是Java开发中不可或缺的一部分,它通过提供两种核心特性——控制反转(IoC)和面向切面编程(AOP)来简化应用的构建。理解并掌握这两种技术对于任何Java开发者来说都至关重要。 **控制反转(IoC)**,也...

    Spring源码

    Spring框架是Java开发中最常用的轻量级开源框架之一,它以IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)为核心,极大地简化了企业级应用的开发工作。在最新版本的...

    Spring2(源码)

    Spring框架是Java开发中最常用的轻量级开源框架之一,它以其强大的依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)能力而著名。源码分析是理解Spring工作原理的关键,能...

    spring-framework-4.2.4.RELEASE-dist Spring资源包

    2. `spring-core`模块:包含了核心工具类,提供基本的反射和类型转换支持,是其他模块的基础。 3. `spring-beans`模块:定义了Bean工厂,是容器的核心,负责Bean的实例化、配置和管理。 4. `spring-aop`模块:实现了...

    解释Spring中的Aop代理

    当我们声明一个Bean为切面并配置通知时,Spring容器会在需要的时候自动创建AOP代理。当通过代理调用目标方法时,Spring会先执行通知,然后转发到实际的目标方法。这个过程称为拦截器链,每个通知相当于一个拦截器,...

    spring-4.3.jar和对应的源文件

    1. spring-core:核心模块,包括IoC容器的基础类、反射工具类和资源加载器等。 2. spring-beans:bean工厂,负责bean的创建、配置和管理。 3. spring-context:上下文模块,扩展了bean工厂,提供了事件发布、国际化...

    手写SpringIOC注解实现版本

    在Spring中,IOC意味着应用程序不再直接创建对象,而是将对象的创建和管理交给了Spring容器。容器根据配置(如XML、Java配置类或注解)来创建和装配这些对象。注解是Spring中的一种重要配置方式,它使得代码更加简洁...

    spring2.5jar包

    8. **spring-core.jar**:核心工具模块,包含了Spring框架的基础类和通用工具,如类型转换系统、反射和资源加载等。 9. **spring-tx.jar**:事务管理模块提供了编程和声明式事务管理的支持,适用于本地事务和分布式...

    Spring + SpringMVC + Mybatis总结,同步博客中的pdf文档

    Spring 的核心特性包括控制反转(Inversion of Control, IoC)和面向切面编程(Aspect-Oriented Programming, AOP)。 【Spring 的优点】 1. **低侵入式设计**:Spring 采用接口驱动的编程方式,使得应用代码对...

    spring原理模拟

    Spring框架是Java开发中不可或缺的一部分,它以依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-Oriented Programming,AOP)为核心,极大地简化了企业级应用的开发。本篇文章将深入探讨Spring框架的...

    Spring 深度阅读

    Spring AOP模块是Spring实现切面编程的关键,它允许开发者定义横切关注点,如日志、事务管理等,并将这些关注点与业务逻辑分离。AOP的使用可以显著减少代码重复,提高代码的复用性。 此外,Spring框架还提供了面向...

    Spring常见面试问题(超详细)

    IOC,即控制反转,它将对象的创建和管理权从代码中分离出来,交由Spring容器负责。这降低了对象间的耦合度,使得开发者可以专注于业务逻辑的编写。DI(依赖注入)是IOC的一种具体实现,通过容器在运行时动态地将依赖...

    Spring 面试题分析_尚硅谷_佟刚

    面向切面编程(AOP)允许开发者将横切关注点(例如日志和事务管理)与业务逻辑分离,通过预定义的方式来减少代码重复,并提高模块化。 二、Spring中Bean的配置和生命周期管理 Spring中的Bean是被IoC容器管理的Java...

    spring-5.3.14-schema.zip

    Spring AOP支持注解和XML配置两种方式来定义切面,使得代码结构更清晰,降低了模块间的耦合。 8. **tx** 事务管理模块提供了一种声明式和编程式的事务处理方式,使得开发者可以轻松地管理事务的边界,保证数据的...

    org.springframework.core.jar

    5. **排序与比较**:`org.springframework.core.order`和`org.springframework.core.type`包提供了排序算法和类型比较机制,对于Spring容器中bean的排序和类型检查起到了关键作用。 6. **事件驱动**:`org.spring...

Global site tag (gtag.js) - Google Analytics