反射的实践

正常请求的接口暂存下来,过一段时间用户做完其他操作后出发这个接口往下执行
目前的逻辑是
    原先的操作接口众多,为了尽量少的对原接口修改,所以使用aop对目标接口进行环绕
同时记录下用户请求的参数和请求的url。
    等到触发操作接口时用url查询到参数,用反射进行方法的执行


反射中遇到的 getMethods 和 getDeclaredMethods 的区别
getMethods:
Returns an array containing Method objects reflecting all the public methods of the class or interface represented by this Class object, including those declared by the class or interface and those inherited from superclasses and superinterfaces.
返回一个数组,其中包含反映此Class对象表示的类或接口的所有公共方法的方法对象,包括由类或接口声明的方法以及从超类和超接口继承的方法。
getDeclaredMethods:
Returns an array containing Method objects reflecting all the declared methods of the class or interface represented by this Class object, including public, protected, default (package) access, and private methods, but excluding inherited methods.
返回一个包含Method对象的数组,该数组反映由这个class对象表示的类或接口的所有声明方法,包括公共、受保护、默认(包)访问和私有方法,但不包括继承的方法。

简言之就是getMethods:只返回公共方法, getDeclaredMethods返回所有方法

1. 发现反射的invoke方法总会返回一个Objec对象, 当反射的方式没有返回值会不会报错?

public void testVoid(){
    System.out.println("sas");
}


Object invoke = method.invoke(new BaseService());

经过测试,无返回值,返回的参数为null, 并且null强行转为Object对象也不会发生异常。

2. 如何记录反射的方法信息?

要定位到方法需要知道方法所在的class类,方法的参数类型。

有两个方案可以实现,

方案一:

1. 记录class全类名,方法名称,方法类型的全类名数组,通过Class.forName获取class类,通过查到getMethod获取到方法。

2. 记录参数为json格式,通过解析json解析为对应类型

3. 获取操作的bean,执行invoke反射

方案二:

依赖于Spring框架,可以定义一个注解,并且在注解上指定关联值。启动的时候通过

public class LoadMethod implements BeanPostProcessor 中
public Object postProcessAfterInitialization(Object bean, String beanName)方法扫描所有类上的注解,收集到一个map中,执行时从map中取出执行method的invoke方法

/**
* 注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperLogAnno {
    /**
     * 关联唯一值
     * @return
     */
    String value();

    /**
     * 参数类型
     * @return
     */
    Class[] paramTypes();
}





/**
*启动扫描注解
*/
@Component
public class LoadMethod implements BeanPostProcessor {
    /**
     * 加载容器
     */
    static Map<String, Object> classesMap = new HashMap<>();

    /**
     * spring加载bean的后置处理器
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> aClass = bean.getClass();
        readMethods(bean,aClass);
        return bean;
    }

    /**
     * 解析注解
     * @param aClass
     */
    public void readMethods(Object bean,Class aClass) {
        Class superclass = aClass.getSuperclass();
        //防止生成代理类后,注解在父类上拿不到注解
        if (superclass != null && superclass != Object.class) {
            readMethods(bean,superclass);
        }
        //返回所有法
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            OperLogAnno annotation = AnnotationUtils.getAnnotation(method, OperLogAnno.class);
            if (annotation != null) {
                String value = annotation.value();
                Class[] paramTypes = annotation.paramTypes();
                classesMap.put(value, new MethodInfo(bean,method,paramTypes));
            }
        }
    }
}






/**
 * 记录方法信息
 */
public class MethodInfo {

    private Object bean;
    private Method method;
    private Class[] paramTypes;

    public MethodInfo(Object bean, Method method, Class[] paramTypes) {
        this.bean = bean;
        this.method = method;
        this.paramTypes = paramTypes;
    }

    /**
     * 获取参数类型
     *
     * @return
     */
    public Class[] getParamTypes() {
        if (paramTypes != null && paramTypes.length > 0) {
            return paramTypes;
        }
        return method.getParameterTypes();
    }

    public Object[] castParamList(List<String> paramList) {
        Class[] paramTypes = getParamTypes();
        Object[] objects = new Object[paramTypes.length];
        for (int i = 0; i < paramTypes.length; i++) {
            Object arg = null;
            if (i < paramList.size()) {
                arg = castparam(paramTypes[i], paramList.get(i));
            }
            objects[i] = arg;
        }
        return objects;
    }

    public Object castparam(Class paramType, String paramStr) {
        if (paramType.isArray()) {
            return JSONObject.parseArray(paramStr, paramType);
        } else {
            try {
                return JSONObject.parseObject(paramStr, paramType);
            } catch (Exception e) {
                return paramStr;
            }
        }
    }

    @SneakyThrows
    public <T> T execute(List<String> paramList) {
        if (paramList == null || paramList.isEmpty()) {
            return (T) this.method.invoke(bean);
        }
        Object[] objects = castParamList(paramList);
        return (T) this.method.invoke(bean, objects);
    }
}

        操作的中间又遇到一个小插曲是,工程中有cglb代理类的生成,导致注解在方法上不会出现在代理类上,查询网上帖子后发现 @Inherited 可以解决,但是该注解只是在class类上才会给让子类也继承父类的注解,方法上根本就不行。所以又发现使用Spring的工具类AnnotationUtils 可以获取到注解,同时还有一个解决办法是通过递归方式循环, 从父类Object下面的那层开始扫描,扫到就覆盖到map中,所以map中最终存储的是层级最低的子类的且拥有注解的方法的相关信息,这符合我们的要求。

OperLogAnno annotation = AnnotationUtils.getAnnotation(method, OperLogAnno.class);
OperLogAnno annotation = method.getAnnotation(OperLogAnno.class);

        后续测试的过程中发现有些参数类型是接口类型接口,参数转对应类型的时候会出问题,所以这里在注解上预留一个参数,用来指定方法的class类型。

        虽然这个问题是解决了,但是还发现了另外一个问题是,有些入参是HttpServletRequest对象,经过排查项目,只是使用HttpServletRequest的getParameterMap()方法获取参数,所以想到的办法是新建一个wrapper类,继承HttpServletRequest的wrapper类,并重写getParameterMap() 指定为我们反射要添加的参数, 这利用父类也可以接口子类的特性就能正常传参,获取参数了。

好了我们可以愉快地玩耍了,等遇到其他问题的时候再排查吧。

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值