精华内容
下载资源
问答
  • 文章目录背景代码自定义异常业务代码异常日志解决方案 背景 在做单元测试时候,在一个私有方法中抛出了自定义异常。使用method.invoke()之后,使用try{} catch (自定义异常){}进行捕获,竟然没有捕获到,最终定位到...

    背景

    在做单元测试时候,在一个私有方法中抛出了自定义异常。使用method.invoke()之后,使用try{} catch (自定义异常){}进行捕获,竟然没有捕获到,最终定位到原因是:如果方法中直接抛出异常,通过反射进行调用时,会抛出InvocationTargetException异常

    代码

    自定义异常

    public class MyException extends RuntimeException {
    
      String msg;
    
      MyException() {
        super();
      }
    
      MyException(String msg) {
        super(msg);
        this.msg = msg;
      }
    }
    

    业务代码

    public class Test {
    
      public static void main(String[] args) throws Exception {
        Test t = new Test();
        Class clz = t.getClass();
        Method addMethod = clz.getDeclaredMethod("divde", int.class, int.class);
        System.out.println(addMethod.invoke(new Test(), 1, 0));
      }
    
      private double divde(int x, int y) {
        if (y == 0) {
          throw new MyException("除数不能为0");
        }
        return 1.0 * x / y;
      }
    }
    

    异常日志

    Exception in thread "main" java.lang.reflect.InvocationTargetException
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at Test.main(Test.java:29)
    Caused by: MyException: 除数不能为0
    	at Test.divde(Test.java:45)
    	... 5 more
    

    解决方案

    通过异常日志我们可以看出来其实最终抛出的是java.lang.reflect.InvocationTargetException异常。通过源码注释我们也可以得出该结论。

    /**
    * @exception InvocationTargetException if the underlying method throws an exception.
    */
    @CallerSensitive
        public Object invoke(Object obj, Object... args)
            throws IllegalAccessException, IllegalArgumentException,
               InvocationTargetException
        {
            if (!override) {
                if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                    Class<?> caller = Reflection.getCallerClass();
                    checkAccess(caller, clazz, obj, modifiers);
                }
            }
            MethodAccessor ma = methodAccessor;             // read volatile
            if (ma == null) {
                ma = acquireMethodAccessor();
            }
            return ma.invoke(obj, args);
        }
    

    通过caused by可以看出该异常是由于自定义异常导致的。我们可以通过ex.getTargetException()获取目标异常。代码以及对应的日志如下:

    public class Test {
      public static void main(String[] args) throws Exception {
        Test t = new Test();
        Class clz = t.getClass();
        Method addMethod = clz.getDeclaredMethod("divde", int.class, int.class);
        try {
          System.out.println(addMethod.invoke(new Test(), 1, 0));
        } catch (InvocationTargetException ex) {
          System.out.println("以下是InvocationTargetException异常:");
          ex.printStackTrace();
          System.out.println("以下是目标异常:");
          ex.getTargetException().printStackTrace();
        }
      }
      
      private double divde(int x, int y) {
        if (y == 0) {
          throw new MyException("除数不能为0");
        }
        return 1.0 * x / y;
      }
    }
    
    以下是InvocationTargetException异常:
    java.lang.reflect.InvocationTargetException
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at Test.main(Test.java:31)
    Caused by: MyException: 除数不能为0
    	at Test.divde(Test.java:46)
    	... 5 more
    以下是目标异常:
    MyException: 除数不能为0
    	at Test.divde(Test.java:46)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at Test.main(Test.java:31)
    
    展开全文
  • 在某一个项目A中使用了某个jar包x.jar, 而x.jar引入了一个类Y, 应该包含y.jar才可以使用Y类。 但是y.jar没有被包含到工程中, 这时候就会在项目A中跑出InvocationTargetException。...

    在某一个项目A中使用了某个jar包x.jar,

    而x.jar引入了一个类Y,

    应该包含y.jar才可以使用Y类。


    但是y.jar没有被包含到工程中,

    这时候就会在项目A中跑出InvocationTargetException。

    展开全文
  • InvocationTargetException来将原来的异常包装起来,通过多加一层间接层的方式来提供统一的访问途径。Java方法可以静态声明它可能会抛出一组固定的异常类型。而反射API里,Method.invoke() 与 Constructor.new...

    InvocationTargetException来将原来的异常包装起来,通过多加一层间接层的方式来提供统一的访问途径。

    Java方法可以静态声明它可能会抛出一组固定的异常类型。而反射API里,Method.invoke() 与 Constructor.newInstance() 这些方法有“双重身份”——它们既代表要调用指定的目标方法,自身也是一个方法;目标方法可能会抛出异常,而它们自身在调用目标方法前也可能会抛出一些异常(例如IllegalArgumentException)。
    它们要调用的目标方法可能抛出任意Throwable类的派生类的异常,但它们自身却不能根据要调用的目标而“动态”改变自己声明要抛出的异常类型,而只能静态声明一组可能抛出的异常类型。
    声明抛出Throwable或Exception的话,这就太宽泛,难以准确反映异常的原因和意图;但不声明成这么宽泛的异常类型的话又无法完整覆盖所有可能由目标方法抛出的异常。
    控制台的打印错误
    Method.invoke() / Constructor.newInstance() 的调用者一个统一的接口,既明确了“这个异常是由目标方法抛出的,不是由我自己抛出的”的意图,又能完整覆盖目标方法所能抛出的所有异常类型(InvocationTargetException.getTargetException() / getCause() 的类型是Throwable)。

    错误原因:在非静态方法中调用static方法的error   

    展开全文
  • 反射的运用

    2019-10-01 15:03:35
    :invoke方法会抛出InvokeTargetException,从而导致无法在原来的try/catch中捕捉到自定义异常, 这里是作者自己遇到的问题,觉得反射导致异常处理比较麻烦 二、具体内容 Class对象 万物皆可对象,所以...

    一、反射介绍

    定义

    在运行时期,能够动态访问、检测完整类结构信息甚至修改类本身的一种能力

    特点

    • 优点
      1. 灵活性高,因为是动态编译。(静态编译即编译器是确定类型&绑定对象;动态编译即运行期确定类型&绑定对象)
      2. 动态编译体现了Java的灵活性、多态特性&降低类之间耦合
    • 缺点
      1. 执行效率低,主要通过JVM执行,所以时间成本会 高于 直接执行相同操作
      2. 反射调用方法是通过invoke方法需要传入Object object和Object… args,
        基本数据类型需要拆装箱,产生大量额外的对象和内存开销,频繁出发GC
      3. 异常处理:invoke方法会抛出InvokeTargetException,从而导致无法在原来的try/catch中捕捉到自定义异常,
        这里是作者自己遇到的问题,觉得反射导致异常处理比较麻烦

    二、具体内容

    Class对象

    万物皆可对象,所以所有的类都有一个类对象。

    java.lang.Class类是反射机制的基础,存放着对应类型对象的运行时信息。

    获取类对象的方法如下

    • Object.getClass():先new出一个对象object,然后object.getClass()
    • T.class:直接通过类(Class)获取,Class.class
    • Class.forName():Class.forName(“类全包名”),例如最熟悉的JDBCClass.forName(“com.mysql.jdbc.Driver”)

    主要使用类和方法

    Class类


    Constructor类(构造器)

    // a. 获取指定的构造函数 (公共 / 继承)
    Constructor<T> getConstructor(Class<?>... parameterTypes)
    // b. 获取所有的构造函数(公共 / 继承) 
    Constructor<?>[] getConstructors(); 
    // c. 获取指定的构造函数 ( 不包括继承)
    Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
    // d. 获取所有的构造函数( 不包括继承)
    Constructor<?>[] getDeclaredConstructors(); 
    

    Fields类(属性或者叫域)

    // a. 获取指定的属性(公共 / 继承)
    Field getField(String name) ;
    // b. 获取所有的属性(公共 / 继承)
    Field[] getFields() ;
    // c. 获取指定的所有属性 (不包括继承)
    Field getDeclaredField(String name) ;
    // d. 获取所有的所有属性 (不包括继承)
    Field[] getDeclaredFields() ;
    // 最终都是获得一个Field类对象
    

    Method类(方法)

    // a. 获取指定的方法(公共 / 继承)
    Method getMethod(String name, Class<?>... parameterTypes) ;
    // b. 获取所有的方法(公共 / 继承)
    Method[] getMethods() ;
    // c. 获取指定的方法 ( 不包括继承)
    Method getDeclaredMethod(String name, Class<?>... parameterTypes) ;
    // d. 获取所有的方法( 不包括继承)
    Method[] getDeclaredMethods() ;
    // 最终都是获得一个Method类对象
    

    父类

    getSuperclass();
    

    创建对象

    Object newInstance(); 
    

    获取类名

    String getName();
    

    三、应用实例

    问题说明

    为了重构一个很古老的项目,我们并不想进行大量的重复工作。
    很难描述具体项目结构,打个比方,我们现在就是想通过方法名来调用某个对象的某个方法。
    我不管你要什么参数,我把我有的所有参数都给你,当然参数名字和类型是一致的。

    问题要点

    要执行一个方法,首先要确定方法,执行对象和参数列表,重中之重就是参数列表
    脑海里设想一下这个大致的流程

    1. 反射获取到该method,然后去获取参数的参数名称列表
    2. 把需要的参数值从所有的参数值通过名字一致过滤出来,放进一个Object[]
    3. 然后menthod.invoke(Object object,Object… args)
    • 大家都知道,java被编译后,参数名称是被擦除的,class文件里面是用arg0,arg1代替,
      幸好1.8后,可以设置编译器参数,将入参参数名保存,这也是这个思路之所以能存在的根本
    • 反射调用方法的时候,参数不是按照名字对应的,而是根据顺序,
      所以你一定要保证参数值列表内的参数前后顺序正确,否则就会方法参数不匹配
      幸好method.getParameters(),获得的参数名称值就是按着顺序来着的。

    这两个幸好下来,自己都感觉自己这个方法啊。真的是非常巧合之下产生。

    四、代码示例

    public class MethodPool {
    
        //做一个方法池,这样可以避免多次创建方法,减少开支
        private static Map<String, Method> methodMap = new HashMap<>();
    
    
        public static Response invoke(Object sourceObject, CommonParameters inParameters) throws IllegalAccessException, IOException {
    
            //包括父类的域也要统计出来,CommonParameters是一个父类被其他的controller统一参数类继承
            //其实这里感觉很神奇,其实应该说是向上转型,子转父,但是依旧保存了子的域,多态很厉害
            Field[] fields = getFieldFromClass(inParameters.getClass());
    
            Method method = getMethodByName(sourceObject, inParameters.getMethodName());
            
            if (method == null) {
                throw new RuntimeException("no such method");
            }
    
            //Get the parameters required for method execution
            //获取到方法执行需要的参数值
            Parameter[] parameters = method.getParameters();
    
            //set access and to map
            //设置域访问限制和映射到map
            Map<String, Field> paramFileMap = Stream.of(fields).peek(it -> it.setAccessible(true))
                    .collect(Collectors.toMap(Field::getName, Function.identity()));
    
    
            Object[] obejects = new Object[parameters.length];
    
            //get all the parameters you need by filter
            //根据参数名称过滤并且获得参数值放入objects
            for (int i = 0; i < parameters.length; i++) {
                //System.out.println(parameters[i].getName());
                Field field = paramFileMap.get(parameters[i].getName());
                if (ObjectUtils.isEmpty(fields)) {
                    throw new RuntimeException("no parameter named " + parameters[i].getName() + "can be found");
                }
                obejects[i] = field.get(inParameters);
                String log = ObjectUtils.isEmpty(obejects[i]) ? "null" :
                        " type->" + obejects.getClass().getTypeName() +
                                " value->" + obejects[i];
                System.out.println("objects[" + i + "]:"+log);
            }
    
            //这里就是异常处理的问题,表示很无奈
            //反射调用的异常覆盖了我的原有自定义异常
            //导致我try/catch无法捕捉我的自定义异常
            Response response = new Response();
            Object object = null;
            try {
                object = method.invoke(sourceObject, obejects);
                if(ObjectUtils.isEmpty(object)){
                    object = "true";
                }
                response.setData(object);
            }catch (InvocationTargetException e){
                response.setData("");
                response.setCode(-1);
                if(e.getTargetException().getClass() == NoRecordsReturned.class){
                    response.setMsg(((NoRecordsReturned)e.getTargetException()).msg);
                    response.setData("");
                    response.setCode(0);
                }else if(e.getTargetException().getClass() == ProfileError.class){
                    response.setMsg(((ProfileError)e.getTargetException()).msg);
                }
            }
            return response;
        }
    
        /**
         * get the Filed[] of the class and its parent class
         *
         * @param clazz
         * @return
         */
        private static Field[] getFieldFromClass(Class clazz) {
            Field[] fields = clazz.getDeclaredFields();
    
            Field[] supFileds = clazz.getSuperclass().getDeclaredFields();
    
            return concat(fields, supFileds);
        }
    
        /**
         * merge two arrays
         *
         * @param afields
         * @param bfields
         * @return
         */
        private static Field[] concat(Field[] afields, Field[] bfields) {
            Field[] fields = new Field[afields.length + bfields.length];
            System.arraycopy(afields, 0, fields, 0, afields.length);
            System.arraycopy(bfields, 0, fields, afields.length, bfields.length);
            return fields;
        }
    
        /**
         * Find the corresponding method in the object by the method name
         *
         * @param obj
         * @param methodName
         * @return
         */
        private static Method getMethodByName(Object obj, String methodName) {
            Class<?> aClass = obj.getClass();
            String key = aClass.getName() + "." + methodName;
            //System.out.println(key);
            
            Method method = methodMap.get(key);
            if (ObjectUtils.isEmpty(method)) {
                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method m :
                        declaredMethods) {
                    if (methodName.equals(m.getName())) {
                        methodMap.put(key, m);
                        method = m;
                    }
                }
            }
            return method;
    }
    

    五、结束语

    可能读者读起来有些云里雾里,我的确很难讲清楚项目结构,不过我也就自己存个档
    也有可能说我写的很简陋,那就太好了,希望大家能指正和指导,让知识运用的更加完美
    不过第一次为一个项目写了个工具,我反正感觉还是挺兴奋的,而且的确有减少不少工作量,很有意思。

    展开全文
  • 环境 JBoss 6.0 + MyEclipse 8.6 + MySQL 5.1 + Struts 2.3 + EJB 3.0   问题 启动JBoss出现如下异常:Dispatcher initialization failedjava.lang.RuntimeException: java.lang.reflect.InvocationTargetException...
  • Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,...

空空如也

空空如也

1
收藏数 6
精华内容 2
热门标签
关键字:

invoketargetexception