精华内容
下载资源
问答
  • 2021-07-21 23:23:06
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    import org.junit.Test;
    
    public class MyTest{
    
        public void test(String[] arg){
            for (String string : arg) {
                System.out.println(string);
            }
        }
    
    
        @Test
        public void demo() throws Exception {
            //获取字节码对象
            Class<MyTest> clazz = (Class<MyTest>) Class.forName("online.MyTest");
            //获取一个对象
            Constructor con =  clazz.getConstructor();
            MyTest m = (MyTest) con.newInstance();
            String[] s = new String[]{"aa","bb"};
            //获取Method对象
            Method method = clazz.getMethod("test", String[].class);
            //调用invoke方法来调用
            method.invoke(m, (Object) s);
        }
    }

     

    更多相关内容
  • java反射invoke方法的使用

    千次阅读 2020-03-26 08:25:11
    invoke 主要是用来调用某个类中的方法的,但是他不是通过当前类直接去调用而是通过反射的机制去调用。 在这之前我们先新建一个实体类,一会会用到。 public class TestMode { private static final String TAG =...

    invoke 主要是用来调用某个类中的方法的,但是他不是通过当前类直接去调用而是通过反射的机制去调用。

    在这之前我们先新建一个实体类,一会会用到。

    public class TestMode {
        private static final String TAG = "TestMode";
    
        private int age;
    
        String name;
    
    
        int length;
        public TestMode() {
        }
    
        public TestMode(int age, String name, int length) {
            this.age = age;
            this.name = name;
            this.length = length;
        }
    
        public int getLength() {
            return length;
        }
    
        public void setLength(int length) {
            this.length = length;
        }
    
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        private void testReflect(String msg) {
            System.out.println(msg);
        }
    
        @Override
        public String toString() {
            return "TestMode{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", length=" + length +
                    '}';
        }
    }
    

     

    然后我们上个测试类:

    public class ReflectTest {
        public static void main(String args[]) {
            TestMode testMode = new TestMode(30, "明", 170);
            //获取class 对象
            Class cla = testMode.getClass();
            //获取去类中的所有方法,返回一个method数组
            Method[] methods = cla.getDeclaredMethods();
            for (Method method : methods) {
                method.setAccessible(true);
                //获取当前方法的修饰符参数
                int modifiers = method.getModifiers();
                //获取该方法的参数
                Class<?>[] types= method.getParameterTypes();
               //只调用私有方法,并且参数必须是一个
                if (modifiers == Modifier.PRIVATE && types.length==1) {
                    try {
    
                        method.invoke(testMode, "我要开始调用方法了" + method.getName());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
    
            }
    
        }
    }

    执行结果:

    我要开始调用方法了testReflect
    
    1. getDeclaredMethods()  是Class 类的方法,它返回了当前类的所有的方法数组 Method[]数组,
    Method是方法相关参数的封装类。

    2. getModifiers() 是method 类中的方法,通过该方法我们可以获取该方法的修饰符 类如 public 等,

    java 中Modifier 类定义了所有的修饰符。

    3. getParameterTypes() 是method 类中的方法,通过该方法能够返回当前方法的所有参数的class[] 数组,并且是顺序的。

    4. invoke(Object obj, Object... args) 是method 类中的方法,这个方法是一个native 方法

      obj: 调用类的实例对象

      args:调用发方法的参数,是可变长度的

    通过 method.invoke(obj , args ) 可以实现method 方法的调用,并通过args 参数传参。

     

     

     

    展开全文
  • 主要介绍了Java反射机制及Method.invoke详解,本文讲解了JAVA反射机制、得到某个对象的属性、得到某个类的静态属性、执行某对象的方法、执行某个类的静态方法等内容,需要的朋友可以参考下
  • Java反射之Method的invoke方法实现

    千次阅读 2020-11-11 21:55:35
    在框架中经常会会用到method.invoke()方法,用来执行某个的对象的目标方法。以前写代码用到反射时,...本文将从java和JVM的源码实现深入探讨invoke方法的实现过程。 首先给出invoke方法多态特性的演示代码: public

    使用reflect(反射)包下面的Field和Method类获得类的属性和方法,并对属性和方法进行操作。

    在框架中经常会会用到method.invoke()方法,用来执行某个的对象的目标方法。以前写代码用到反射时,总是获取先获取Method,然后传入对应的Class实例对象执行方法。然而前段时间研究invoke方法时,发现invoke方法居然包含多态的特性,这是以前没有考虑过的一个问题。那么Method.invoke()方法的执行过程是怎么实现的?它的多态又是如何实现的呢?

    本文将从java和JVM的源码实现深入探讨invoke方法的实现过程。

    首先给出invoke方法多态特性的演示代码:

    public class MethodInvoke {
    	public static void main(String[] args) throws Exception {
    		Method animalMethod = Animal.class.getDeclaredMethod("print");
    		Method catMethod = Cat.class.getDeclaredMethod("print");
    	
    		Animal animal = new Animal();
    		Cat cat = new Cat();
    		animalMethod.invoke(cat);
    		animalMethod.invoke(animal);
    		
    		catMethod.invoke(cat);
    		catMethod.invoke(animal);
    	}
    }
     
    class Animal {
    	public void print() {
    		System.out.println("Animal.print()");
    	}
    }
     
    class Cat extends Animal {
    	@Override
    	public void print() {
    		System.out.println("Cat.print()");
    	}
    }

    代码中,Cat类覆盖了父类Animal的print()方法, 然后通过反射分别获取print()的Method对象。最后分别用Cat和Animal的实例对象去执行print()方法。其中animalMethod.invoke(animal)和catMethod.invoke(cat),示例对象的真实类型和Method的声明Classs是相同的,按照预期打印结果;animalMethod.invoke(cat)中,由于Cat是Animal的子类,按照多态的特性,子类调用父类的的方法,方法执行时会动态链接到子类的实现方法上。因此,这里会调用Cat.print()方法;而catMethod.invoke(animal)中,传入的参数类型Animal是父类,却期望调用子类Cat的方法,因此这一次会抛出异常。代码打印结果为:

    Cat.print()
    Animal.print()
    Cat.print()
    Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    	at java.lang.reflect.Method.invoke(Unknown Source)
    	at com.wy.invoke.MethodInvoke.main(MethodInvoke.java:17)

    接下来,我们来看看invoke()方法的实现过程。

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

    invoke()方法中主要分为两部分:访问控制检查和调用MethodAccessor.invoke()实现方法执行。

    首先看一下访问控制检查这一块的逻辑。第一眼看到这里的逻辑的时候,很容易搞不清楚是干嘛的。通俗来讲就是通过方法的修饰符(public/protected/private/package),来判断方法的调用者是否可以访问该方法。这是java的基础内容,不过用代码写出来,一下子不容易想到。访问控制检查分为3步:

    1. 检查override,如果override为true,跳过检查;否则继续;
    2. 快速检查,判断该方法的修饰符modifiers是否为public,如果是跳过检查;否则继续;
    3. 详细检查,通过方法的(protected/private/package)修饰符或方法的声明类(例如子类可以访问父类的protected方法)与调用者caller之间的关系,判断caller是否有权限访问该方法。

    override属性是Method的父类AccessibleObject中声明的变量,使得程序可以控制是否跳过访问权限的检查。另外,Method的实例对象中,override属性的初始值设置为false。

    public void setAccessible(boolean flag) throws SecurityException {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
            setAccessible0(this, flag);
        }
     
        private static void setAccessible0(AccessibleObject obj, boolean flag)
            throws SecurityException
        {
            if (obj instanceof Constructor && flag == true) {
                Constructor<?> c = (Constructor<?>)obj;
                if (c.getDeclaringClass() == Class.class) {
                    throw new SecurityException("Can not make a java.lang.Class" +
                                            " constructor accessible");
            }
        }
        obj.override = flag;
    }

    多说一句,Field同样继承了AccessibleObject,且Field的override也是初始化为false的,也就是说并没有按照变量的修饰符去初始化不同的值。但是我们在调用Field.set(Object obj, Object value)时,如果该Field是private修饰的,会因没有访问权限而抛出异常,因此必须调用setAccessible(true)。此处非常容易理解为因为变量是public的,所以override就被初始化为true。

    invoke()方法中,访问控制检查之后,就是通过MethodAccessor.invoke()调用方法。再来看一下代码:

    MethodAccessor ma = methodAccessor;             // read volatile
    if (ma == null) {
        ma = acquireMethodAccessor();
    }
    return ma.invoke(obj, args);

    这里的逻辑很简单,首先将变量methodAccessor赋值给ma,在方法栈中保存一个可以直接引用的本地变量,如果methodAccessor不存在,调用acquireMethodAccessor()方法创建一个。

        private volatile MethodAccessor methodAccessor;
        private Method root;
        
        private MethodAccessor acquireMethodAccessor() {
            // First check to see if one has been created yet, and take it
            // if so
            MethodAccessor tmp = null;
            if (root != null) tmp = root.getMethodAccessor();
            if (tmp != null) {
                methodAccessor = tmp;
            } else {
                // Otherwise fabricate one and propagate it up to the root
                tmp = reflectionFactory.newMethodAccessor(this);
                setMethodAccessor(tmp);
            }
     
            return tmp;
        }
     
        void setMethodAccessor(MethodAccessor accessor) {
            methodAccessor = accessor;
            // Propagate up
            if (root != null) {
                root.setMethodAccessor(accessor);
            }
        }
     
        Method copy() {
            Method res = new Method(clazz, name, parameterTypes, returnType,
                                    exceptionTypes, modifiers, slot, signature,
                                    annotations, parameterAnnotations, annotationDefault);
            res.root = this;
            res.methodAccessor = methodAccessor;
            return res;
        }

    综合acquireMethodAccessor(),setMethodAccessor()以及copy()这三个方法,可以看到一个Method实例对象维护了一个root引用。当调用Method.copy()进行方法拷贝时,root指向了被拷贝的对象。那么当一个Method被多次拷贝后,调用一次setMethodAccessor()方法,就会将root引用所指向的Method的methodAccessor变量同样赋值。例如:D -> C -> B -> A,其中X-> Y表示X = Y.copy(), 当C对象调用setMethodAccessor()时,B和A都会传播赋值methodAccessor, 而D的methodAccessor还是null。紧接着,当D需要获取methodAccessor而调用acquireMethodAccessor()时,D获取root的methodAccessor, 那么D将和ABC持有相同的methodAccessor。

    虽然Method中,通过维护root引用意图使相同的方法始终保持只有一个methodAccessor实例,但是上述方法仍然无法保证相同的方法只有一个methodAccessor实例。例如通过copy()使ABCD保持关系:D -> C -> B -> A, 当B对象调用setMethodAccessor()时,B和A都会赋值methodAccessor, 而C、D的methodAccessor还是null。这时D调用acquireMethodAccessor()时,D获取root也就是C的methodAccessor,发现为空,然后就新创建了一个。从而出现了相同的方法中出现了两个methodAccessor实例对象的现象。

    在Class.getMethod()、Class.getDeclaredMethod()以及Class.getDeclaredMethod(String name, Class<?>... parameterTypes)方法中最终都会调用copy()方法来保障Method使用的安全性。 在比较极端加巧合的情况下,可能会引起类膨胀的问题,这就是接下来要讲到的MethodAccessor的实现机制。

    在前面代码中,MethodAccessor的创建是通过反射工厂ReflectionFactory的newMethodAccessor(Method)方法来创建的。

    public MethodAccessor newMethodAccessor(Method method) {
            checkInitted();
     
        if (noInflation) {
            return new MethodAccessorGenerator().
                generateMethod(method.getDeclaringClass(),
                               method.getName(),
                               method.getParameterTypes(),
                               method.getReturnType(),
                               method.getExceptionTypes(),
                               method.getModifiers());
        } else {
            NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
            DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
            acc.setParent(res);
            return res;
        }
    }

    其中, checkInitted()方法检查从配置项中读取配置并设置noInflation、inflationThreshold的值:

     

    浪费时间

    直接上链接:https://blog.csdn.net/wenyuan65/article/details/81145900

     

    展开全文
  • java反射之Method的invoke方法实现

    万次阅读 多人点赞 2018-07-29 00:31:48
    然而前段时间研究invoke方法时,发现invoke方法居然包含多态的特性,这是以前没有考虑过的一个问题。那么Method.invoke()方法的执行过程是怎么实现的?它的多态又是如何实现的呢? 本文将从java和JVM...

    在框架中经常会会用到method.invoke()方法,用来执行某个的对象的目标方法。以前写代码用到反射时,总是获取先获取Method,然后传入对应的Class实例对象执行方法。然而前段时间研究invoke方法时,发现invoke方法居然包含多态的特性,这是以前没有考虑过的一个问题。那么Method.invoke()方法的执行过程是怎么实现的?它的多态又是如何实现的呢?

    本文将从java和JVM的源码实现深入探讨invoke方法的实现过程。

    首先给出invoke方法多态特性的演示代码:

    public class MethodInvoke {
    
    	public static void main(String[] args) throws Exception {
    		Method animalMethod = Animal.class.getDeclaredMethod("print");
    		Method catMethod = Cat.class.getDeclaredMethod("print");
    		
    		Animal animal = new Animal();
    		Cat cat = new Cat();
    		animalMethod.invoke(cat);
    		animalMethod.invoke(animal);
    		
    		catMethod.invoke(cat);
    		catMethod.invoke(animal);
    	}
    	
    }
    
    class Animal {
    	
    	public void print() {
    		System.out.println("Animal.print()");
    	}
    	
    }
    
    class Cat extends Animal {
    	
    	@Override
    	public void print() {
    		System.out.println("Cat.print()");
    	}
    	
    }

    代码中,Cat类覆盖了父类Animal的print()方法, 然后通过反射分别获取print()的Method对象。最后分别用Cat和Animal的实例对象去执行print()方法。其中animalMethod.invoke(animal)和catMethod.invoke(cat),示例对象的真实类型和Method的声明Classs是相同的,按照预期打印结果;animalMethod.invoke(cat)中,由于Cat是Animal的子类,按照多态的特性,子类调用父类的的方法,方法执行时会动态链接到子类的实现方法上。因此,这里会调用Cat.print()方法;而catMethod.invoke(animal)中,传入的参数类型Animal是父类,却期望调用子类Cat的方法,因此这一次会抛出异常。代码打印结果为:

    Cat.print()
    Animal.print()
    Cat.print()
    Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    	at java.lang.reflect.Method.invoke(Unknown Source)
    	at com.wy.invoke.MethodInvoke.main(MethodInvoke.java:17)

    接下来,我们来看看invoke()方法的实现过程。

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

    invoke()方法中主要分为两部分:访问控制检查和调用MethodAccessor.invoke()实现方法执行。

    首先看一下访问控制检查这一块的逻辑。第一眼看到这里的逻辑的时候,很容易搞不清楚是干嘛的。通俗来讲就是通过方法的修饰符(public/protected/private/package),来判断方法的调用者是否可以访问该方法。这是java的基础内容,不过用代码写出来,一下子不容易想到。访问控制检查分为3步:

    1. 检查override,如果override为true,跳过检查;否则继续;
    2. 快速检查,判断该方法的修饰符modifiers是否为public,如果是跳过检查;否则继续;
    3. 详细检查,通过方法的(protected/private/package)修饰符或方法的声明类(例如子类可以访问父类的protected方法)与调用者caller之间的关系,判断caller是否有权限访问该方法。

    override属性是Method的父类AccessibleObject中声明的变量,使得程序可以控制是否跳过访问权限的检查。另外,Method的实例对象中,override属性的初始值设置为false。

        public void setAccessible(boolean flag) throws SecurityException {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
            setAccessible0(this, flag);
        }
    
        private static void setAccessible0(AccessibleObject obj, boolean flag)
            throws SecurityException
        {
            if (obj instanceof Constructor && flag == true) {
                Constructor<?> c = (Constructor<?>)obj;
                if (c.getDeclaringClass() == Class.class) {
                    throw new SecurityException("Can not make a java.lang.Class" +
                                                " constructor accessible");
                }
            }
            obj.override = flag;
        }

    多说一句,Field同样继承了AccessibleObject,且Field的override也是初始化为false的,也就是说并没有按照变量的修饰符去初始化不同的值。但是我们在调用Field.set(Object obj, Object value)时,如果该Field是private修饰的,会因没有访问权限而抛出异常,因此必须调用setAccessible(true)。此处非常容易理解为因为变量是public的,所以override就被初始化为true。

    invoke()方法中,访问控制检查之后,就是通过MethodAccessor.invoke()调用方法。再来看一下代码:

            MethodAccessor ma = methodAccessor;             // read volatile
            if (ma == null) {
                ma = acquireMethodAccessor();
            }
            return ma.invoke(obj, args);

    这里的逻辑很简单,首先将变量methodAccessor赋值给ma,在方法栈中保存一个可以直接引用的本地变量,如果methodAccessor不存在,调用acquireMethodAccessor()方法创建一个。

        private volatile MethodAccessor methodAccessor;
        private Method root;
        
        private MethodAccessor acquireMethodAccessor() {
            // First check to see if one has been created yet, and take it
            // if so
            MethodAccessor tmp = null;
            if (root != null) tmp = root.getMethodAccessor();
            if (tmp != null) {
                methodAccessor = tmp;
            } else {
                // Otherwise fabricate one and propagate it up to the root
                tmp = reflectionFactory.newMethodAccessor(this);
                setMethodAccessor(tmp);
            }
    
            return tmp;
        }
    
        void setMethodAccessor(MethodAccessor accessor) {
            methodAccessor = accessor;
            // Propagate up
            if (root != null) {
                root.setMethodAccessor(accessor);
            }
        }
    
        Method copy() {
            Method res = new Method(clazz, name, parameterTypes, returnType,
                                    exceptionTypes, modifiers, slot, signature,
                                    annotations, parameterAnnotations, annotationDefault);
            res.root = this;
            res.methodAccessor = methodAccessor;
            return res;
        }

    综合acquireMethodAccessor(),setMethodAccessor()以及copy()这三个方法,可以看到一个Method实例对象维护了一个root引用。当调用Method.copy()进行方法拷贝时,root指向了被拷贝的对象。那么当一个Method被多次拷贝后,调用一次setMethodAccessor()方法,就会将root引用所指向的Method的methodAccessor变量同样赋值。例如:D -> C -> B -> A,其中X-> Y表示X = Y.copy(), 当C对象调用setMethodAccessor()时,B和A都会传播赋值methodAccessor, 而D的methodAccessor还是null。紧接着,当D需要获取methodAccessor而调用acquireMethodAccessor()时,D获取root的methodAccessor, 那么D将和ABC持有相同的methodAccessor。

    虽然Method中,通过维护root引用意图使相同的方法始终保持只有一个methodAccessor实例,但是上述方法仍然无法保证相同的方法只有一个methodAccessor实例。例如通过copy()使ABCD保持关系:D -> C -> B -> A, 当B对象调用setMethodAccessor()时,B和A都会赋值methodAccessor, 而C、D的methodAccessor还是null。这时D调用acquireMethodAccessor()时,D获取root也就是C的methodAccessor,发现为空,然后就新创建了一个。从而出现了相同的方法中出现了两个methodAccessor实例对象的现象。

    在Class.getMethod()、Class.getDeclaredMethod()以及Class.getDeclaredMethod(String name, Class<?>... parameterTypes)方法中最终都会调用copy()方法来保障Method使用的安全性。 在比较极端加巧合的情况下,可能会引起类膨胀的问题,这就是接下来要讲到的MethodAccessor的实现机制。

    copy

    在前面代码中,MethodAccessor的创建是通过反射工厂ReflectionFactory的newMethodAccessor(Method)方法来创建的。

        public MethodAccessor newMethodAccessor(Method method) {
            checkInitted();
    
            if (noInflation) {
                return new MethodAccessorGenerator().
                    generateMethod(method.getDeclaringClass(),
                                   method.getName(),
                                   method.getParameterTypes(),
                                   method.getReturnType(),
                                   method.getExceptionTypes(),
                                   method.getModifiers());
            } else {
                NativeMethodAccessorImpl acc =
                    new NativeMethodAccessorImpl(method);
                DelegatingMethodAccessorImpl res =
                    new DelegatingMethodAccessorImpl(acc);
                acc.setParent(res);
                return res;
            }
        }

    其中, checkInitted()方法检查从配置项中读取配置并设置noInflation、inflationThreshold的值:

        private static void checkInitted() {
            if (initted) return;
            AccessController.doPrivileged(
                new PrivilegedAction<Void>() {
                    public Void run() {
    
                        if (System.out == null) {
                            // java.lang.System not yet fully initialized
                            return null;
                        }
    
                        String val = System.getProperty("sun.reflect.noInflation");
                        if (val != null && val.equals("true")) {
                            noInflation = true;
                        }
    
                        val = System.getProperty("sun.reflect.inflationThreshold");
                        if (val != null) {
                            try {
                                inflationThreshold = Integer.parseInt(val);
                            } catch (NumberFormatException e) {
                                throw (RuntimeException)
                                    new RuntimeException("Unable to parse property sun.reflect.inflationThreshold").
                                        initCause(e);
                            }
                        }
    
                        initted = true;
                        return null;
                    }
                });
        }

    可以通过启动参数-Dsun.reflect.noInflation=false -Dsun.reflect.inflationThreshold=15来设置:

    结合字面意思及下面代码理解,这两个配置sun.reflect.noInflation是控制是否立即进行类膨胀,sun.reflect.inflationThreshold是设置类膨胀阈值。

    创建MethodAccessor有两种选择,一种是当sun.reflect.noInflation配置项为true时,ReflectionFactory利用MethodAccessor的字节码生成类 MethodAccessorGenerator直接创建一个代理类,通过间接调用原方法完成invoke()任务,具体实现稍后给出。MethodAccessor的另一种实现方式是,创建DelegatingMethodAccessorImpl 委托类,并将执行invoke()方法的具体内容交由NativeMethodAccessorImpl实现,而NativeMethodAccessorImpl最终调用native方法完成invoke()任务。以下是NativeMethodAccessorImpl的invoke()方法实现。

        public Object invoke(Object obj, Object[] args) 
            throws IllegalArgumentException, InvocationTargetException
        {
            if (++numInvocations > ReflectionFactory.inflationThreshold()) {
                MethodAccessorImpl acc = (MethodAccessorImpl)
                    new MethodAccessorGenerator().
                        generateMethod(method.getDeclaringClass(),
                                       method.getName(),
                                       method.getParameterTypes(),
                                       method.getReturnType(),
                                       method.getExceptionTypes(),
                                       method.getModifiers());
                parent.setDelegate(acc);
            }
    
            return invoke0(method, obj, args);
        }
    
        private static native Object invoke0(Method m, Object obj, Object[] args);

    可以看到,当numInvocations数量大于配置项sun.reflect.inflationThreshold即类膨胀阈值时, 使用MethodAccessorGenerator创建一个代理类对象,并且将被委托的NativeMethodAccessorImpl的parent,也就是委托类DelegatingMethodAccessorImpl的委托类设置为这个生成的代理对象。这么说可能有点绕,下面用一幅图表示这个过程。

    delegate

    总体来说,当调用invoke()时,按照默认配置,Method首先创建一个DelegatingMethodAccessorImpl对象,并设置一个被委托的NativeMethodAccessorImpl对象,那么method.invoke()就被转换成DelegatingMethodAccessorImpl.invoke(),然后又被委托给NativeMethodAccessorImp.invoke()实现。当NativeMethodAccessorImp.invoke()调用次数超过一定热度时(默认15次),被委托方又被转换成代理类来实现。

    之前提到过在极端情况下,同一个方法的Method对象存在多个不同拷贝拷贝时,可能存在多个MethodAccessor对象。那么当多次调用后,必然会生成两个重复功能的代理类。当然,一般情况下,生成两个代理类并没有较大的影响。

    其中代理类的具体字节码实现过程较为复杂,大体思想是生成一个如下所示的类:

    public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
    
    	public GeneratedMethodAccessor1 () {
    	    super();
    	}
    	
    	public Object invoke(Object obj, Object[] args)
    	        throws IllegalArgumentException, InvocationTargetException 
    	{
    		if (!(obj instanceof Cat)) {
    			throw new ClassCastException();
    		}
    		if (args != null && args.length != 0) {
    			throw new IllegalArgumentException();
    		}
    		
    		try {
    			Cat cat = (Cat) obj;
    			cat.print();
    			return null;
    		} catch (Throwable e) {
    			throw new InvocationTargetException(e, "invoke error");
    		}
    	}
    	
    }

    到目前为止,除了在代理的GeneratedMethodAccessor1 类中,方法的执行有多态的特性,而NativeMethodAccessorImp的invoke()实现是在jdk中的完成的。接下来我们将目光移到NativeMethodAccessorImp的native方法invoke0();

    openJDK下载地址

    首先,在\jdk\src\share\native\sun\reflect路径下找到NativeAccessors.c, 其中有Java_sun_reflect_NativeMethodAccessorImpl _invoke0()方法,根据JNI定义函数名的规则"包名_类名_方法名",这就是我们要找的native方法实现入口。

    JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0
    (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)
    {
        return JVM_InvokeMethod(env, m, obj, args);
    }

    方法调用JVM_InvokeMethod(), 一般以JVM_开头的函数定义在jvm.cpp文件中,不熟悉的话可以通过头文件jvm.h看出来。继续追踪,发现jvm.cpp文件位于spot\src\share\vm\prims文件夹下。

    JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))
      JVMWrapper("JVM_InvokeMethod");
      Handle method_handle;
      if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) {
        method_handle = Handle(THREAD, JNIHandles::resolve(method));
        Handle receiver(THREAD, JNIHandles::resolve(obj));
        objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));
        oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL);
        jobject res = JNIHandles::make_local(env, result);
        if (JvmtiExport::should_post_vm_object_alloc()) {
          oop ret_type = java_lang_reflect_Method::return_type(method_handle());
          assert(ret_type != NULL, "sanity check: ret_type oop must not be NULL!");
          if (java_lang_Class::is_primitive(ret_type)) {
            // Only for primitive type vm allocates memory for java object.
            // See box() method.
            JvmtiExport::post_vm_object_alloc(JavaThread::current(), result);
          }
        }
        return res;
      } else {
        THROW_0(vmSymbols::java_lang_StackOverflowError());
      }
    JVM_END

    其中oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL)是方法的执行过程,在\hotspot\src\share\vm\runtime路径下找到reflection.cpp文件。

    oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) {
      oop mirror             = java_lang_reflect_Method::clazz(method_mirror);
      int slot               = java_lang_reflect_Method::slot(method_mirror);
      bool override          = java_lang_reflect_Method::override(method_mirror) != 0;
      objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror)));
    
      oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror);
      BasicType rtype;
      if (java_lang_Class::is_primitive(return_type_mirror)) {
        rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL);
      } else {
        rtype = T_OBJECT;
      }
    
      instanceKlassHandle klass(THREAD, java_lang_Class::as_Klass(mirror));
      Method* m = klass->method_with_idnum(slot);
      if (m == NULL) {
        THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");
      }
      methodHandle method(THREAD, m);
    
      return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD);
    }
    
    oop Reflection::invoke(instanceKlassHandle klass, methodHandle reflected_method,
                           Handle receiver, bool override, objArrayHandle ptypes,
                           BasicType rtype, objArrayHandle args, bool is_method_invoke, TRAPS) {
      ResourceMark rm(THREAD);
    
      methodHandle method;      // actual method to invoke
      KlassHandle target_klass; // target klass, receiver's klass for non-static
    
      // Ensure klass is initialized
      klass->initialize(CHECK_NULL);
    
      bool is_static = reflected_method->is_static();
      if (is_static) {
        // ignore receiver argument
        method = reflected_method;
        target_klass = klass;
      } else {
        // check for null receiver
        if (receiver.is_null()) {
          THROW_0(vmSymbols::java_lang_NullPointerException());
        }
        // Check class of receiver against class declaring method
        if (!receiver->is_a(klass())) {
          THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "object is not an instance of declaring class");
        }
        // target klass is receiver's klass
        target_klass = KlassHandle(THREAD, receiver->klass());
        // no need to resolve if method is private or <init>
        if (reflected_method->is_private() || reflected_method->name() == vmSymbols::object_initializer_name()) {
          method = reflected_method;
        } else {
          // resolve based on the receiver
          if (reflected_method->method_holder()->is_interface()) {
            // resolve interface call
            if (ReflectionWrapResolutionErrors) {
              // new default: 6531596
              // Match resolution errors with those thrown due to reflection inlining
              // Linktime resolution & IllegalAccessCheck already done by Class.getMethod()
              method = resolve_interface_call(klass, reflected_method, target_klass, receiver, THREAD);
              if (HAS_PENDING_EXCEPTION) {
              // Method resolution threw an exception; wrap it in an InvocationTargetException
                oop resolution_exception = PENDING_EXCEPTION;
                CLEAR_PENDING_EXCEPTION;
                JavaCallArguments args(Handle(THREAD, resolution_exception));
                THROW_ARG_0(vmSymbols::java_lang_reflect_InvocationTargetException(),
                    vmSymbols::throwable_void_signature(),
                    &args);
              }
            } else {
              method = resolve_interface_call(klass, reflected_method, target_klass, receiver, CHECK_(NULL));
            }
          }  else {
            // if the method can be overridden, we resolve using the vtable index.
            assert(!reflected_method->has_itable_index(), "");
            int index = reflected_method->vtable_index();
            method = reflected_method;
            if (index != Method::nonvirtual_vtable_index) {
              // target_klass might be an arrayKlassOop but all vtables start at
              // the same place. The cast is to avoid virtual call and assertion.
              InstanceKlass* inst = (InstanceKlass*)target_klass();
              method = methodHandle(THREAD, inst->method_at_vtable(index));
            }
            if (!method.is_null()) {
              // Check for abstract methods as well
              if (method->is_abstract()) {
                // new default: 6531596
                if (ReflectionWrapResolutionErrors) {
                  ResourceMark rm(THREAD);
                  Handle h_origexception = Exceptions::new_exception(THREAD,
                         vmSymbols::java_lang_AbstractMethodError(),
                         Method::name_and_sig_as_C_string(target_klass(),
                         method->name(),
                         method->signature()));
                  JavaCallArguments args(h_origexception);
                  THROW_ARG_0(vmSymbols::java_lang_reflect_InvocationTargetException(),
                    vmSymbols::throwable_void_signature(),
                    &args);
                } else {
                  ResourceMark rm(THREAD);
                  THROW_MSG_0(vmSymbols::java_lang_AbstractMethodError(),
                            Method::name_and_sig_as_C_string(target_klass(),
                                                                    method->name(),
                                                                    method->signature()));
                }
              }
            }
          }
        }
      }
    
      // I believe this is a ShouldNotGetHere case which requires
      // an internal vtable bug. If you ever get this please let Karen know.
      if (method.is_null()) {
        ResourceMark rm(THREAD);
        THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(),
                    Method::name_and_sig_as_C_string(klass(),
                                                            reflected_method->name(),
                                                            reflected_method->signature()));
      }
    
      // In the JDK 1.4 reflection implementation, the security check is
      // done at the Java level
      if (!(JDK_Version::is_gte_jdk14x_version() && UseNewReflection)) {
    
      // Access checking (unless overridden by Method)
      if (!override) {
        if (!(klass->is_public() && reflected_method->is_public())) {
          bool access = Reflection::reflect_check_access(klass(), reflected_method->access_flags(), target_klass(), is_method_invoke, CHECK_NULL);
          if (!access) {
            return NULL; // exception
          }
        }
      }
    
      } // !(Universe::is_gte_jdk14x_version() && UseNewReflection)
    
      assert(ptypes->is_objArray(), "just checking");
      int args_len = args.is_null() ? 0 : args->length();
      // Check number of arguments
      if (ptypes->length() != args_len) {
        THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "wrong number of arguments");
      }
    
      // Create object to contain parameters for the JavaCall
      JavaCallArguments java_args(method->size_of_parameters());
    
      if (!is_static) {
        java_args.push_oop(receiver);
      }
    
      for (int i = 0; i < args_len; i++) {
        oop type_mirror = ptypes->obj_at(i);
        oop arg = args->obj_at(i);
        if (java_lang_Class::is_primitive(type_mirror)) {
          jvalue value;
          BasicType ptype = basic_type_mirror_to_basic_type(type_mirror, CHECK_NULL);
          BasicType atype = unbox_for_primitive(arg, &value, CHECK_NULL);
          if (ptype != atype) {
            widen(&value, atype, ptype, CHECK_NULL);
          }
          switch (ptype) {
            case T_BOOLEAN:     java_args.push_int(value.z);    break;
            case T_CHAR:        java_args.push_int(value.c);    break;
            case T_BYTE:        java_args.push_int(value.b);    break;
            case T_SHORT:       java_args.push_int(value.s);    break;
            case T_INT:         java_args.push_int(value.i);    break;
            case T_LONG:        java_args.push_long(value.j);   break;
            case T_FLOAT:       java_args.push_float(value.f);  break;
            case T_DOUBLE:      java_args.push_double(value.d); break;
            default:
              THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "argument type mismatch");
          }
        } else {
          if (arg != NULL) {
            Klass* k = java_lang_Class::as_Klass(type_mirror);
            if (!arg->is_a(k)) {
              THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "argument type mismatch");
            }
          }
          Handle arg_handle(THREAD, arg);         // Create handle for argument
          java_args.push_oop(arg_handle); // Push handle
        }
      }
    
      assert(java_args.size_of_parameters() == method->size_of_parameters(), "just checking");
    
      // All oops (including receiver) is passed in as Handles. An potential oop is returned as an
      // oop (i.e., NOT as an handle)
      JavaValue result(rtype);
      JavaCalls::call(&result, method, &java_args, THREAD);
    
      if (HAS_PENDING_EXCEPTION) {
        // Method threw an exception; wrap it in an InvocationTargetException
        oop target_exception = PENDING_EXCEPTION;
        CLEAR_PENDING_EXCEPTION;
        JavaCallArguments args(Handle(THREAD, target_exception));
        THROW_ARG_0(vmSymbols::java_lang_reflect_InvocationTargetException(),
                    vmSymbols::throwable_void_signature(),
                    &args);
      } else {
        if (rtype == T_BOOLEAN || rtype == T_BYTE || rtype == T_CHAR || rtype == T_SHORT)
          narrow((jvalue*) result.get_value_addr(), rtype, CHECK_NULL);
        return box((jvalue*) result.get_value_addr(), rtype, CHECK_NULL);
      }
    }

    Reflection::invoke_method()中调用Reflection::invoke(),然后在Reflection::invoke()方法中,当反射调用的方法是接口方法时,调用Reflection::resolve_interface_call(),该方法依赖LinkResolver::resolve_interface_call()来完成方法的动态链接过程,具体实现就不在这里展示。

    method = resolve_interface_call(klass, reflected_method, target_klass, receiver, CHECK_(NULL));
    methodHandle Reflection::resolve_interface_call(instanceKlassHandle klass, methodHandle method,
                                                    KlassHandle recv_klass, Handle receiver, TRAPS) {
      assert(!method.is_null() , "method should not be null");
    
      CallInfo info;
      Symbol*  signature  = method->signature();
      Symbol*  name       = method->name();
      LinkResolver::resolve_interface_call(info, receiver, recv_klass, klass,
                                           name, signature,
                                           KlassHandle(), false, true,
                                           CHECK_(methodHandle()));
      return info.selected_method();
    }

    如果反射调用的方法是可以被覆盖的方法,例如Animal.print(), Reflection::invoke()最终通过查询虚方法表vtable来确定最终的method。

            // if the method can be overridden, we resolve using the vtable index.
            assert(!reflected_method->has_itable_index(), "");
            int index = reflected_method->vtable_index();
            method = reflected_method;
            if (index != Method::nonvirtual_vtable_index) {
              // target_klass might be an arrayKlassOop but all vtables start at
              // the same place. The cast is to avoid virtual call and assertion.
              InstanceKlass* inst = (InstanceKlass*)target_klass();
              method = methodHandle(THREAD, inst->method_at_vtable(index));
            }

     

    总结

    1.method.invoke()方法支持多态特性,其native实现在方法真正执行之前通过动态连接或者虚方法表来实现。

    2.框架中使用method.invoke()执行方法调用时,初始获取method对象时,可以先调用一次setAccessable(true),使得后面每次调用invoke()时,节省一次方法修饰符的判断,略微提升性能。业务允许的情况下,Field同样可以如此操作。

    3.委托模式可以解决一种方案的多种实现之间自由切换,而代理模式只能根据传入的被代理对象来实现功能。

     

     

    参考文章:

    JAVA深入研究——Method的Invoke方法

    展开全文
  • 最近做项目用到定时任务...在执行invoke方法的时候,一直报空指针异常,百度了一下午才找到解决方案。现在把解决方法分享给大家。先看下代码:首先是一个测试类:@Testpublic void test() {String classBeanName = "...
  • Java反射invoke()方法

    千次阅读 2019-04-23 20:13:13
    invoke方法用来在运行时动态地调用某个实例的方法。它的实现如下: @CallerSensitive public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, ...
  • java反射中的invoke方法

    2020-01-07 00:06:04
    2、invoke方法的含义:反射调用实例方法,返回的是实例方法的返回值。 3、哪个方法调用invoke方法,返回的就是哪个方法的返回值。 4、invoke方法的参数含义:第一个参数的含义是:实例对象,第二个方法的含义是:...
  • 作者:码上猿梦cnblogs.com/daimajun/p/6545533.html推荐阅读(点击即可跳转阅读)先讲一下java中的反射反射就是将类别的各个组成部分进行剖析,可以得到每个组成部分,就可以对每一部分进行操作。在比较复杂的程序...
  • import java.lang.reflect.Method;public class testReflect {/*** @param args*/public static void main(String[] args) {try {Wu w=new Wu();Object[] argspara=new Object[]{};testReflect.invokeMethod(w, "out...
  • import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class JavaReflectDemo1 { public static void main(String[] args) throws NoSuchMethodException, ...
  • java 反射 invoke()

    千次阅读 2021-06-15 15:52:13
    今天在同事的提醒下,突然发现自己对反射的应用不够到位,看了一下网上大神对源码的讲解,结合自己的问题特此记录一下. 背景 : 通过反射的方式获取枚举类的值 代码如下 public RestResult<List<BaseSysDictVO>...
  • java中的反射invoke方法详解

    千次阅读 2021-02-12 11:52:39
    展开全部就是调用类中的方法e68a843231313335323631343130323136353331333365646239,最简单的用法是可以把方法参数化,invoke(class, method)比如你Test类里有一系列名字相似的方法setValue1、setValue2等等。...
  • java中的反射invoke方法

    千次阅读 2021-03-05 18:03:44
    以下代码简单地介绍了java反射invoke方法packageorg.curry.tool;importjava.lang.reflect.Method;publicclassInvokeMethods{publicstaticvoidmain(String[]args){Employeeemp=newEmployee(...
  • 用传统的OOP思想来说,任何一个你写好的且编译过的生成的Class文件,在被类加载器加载后,都会对应有一个java.lang.Class这个类的实例。所以说,每个类的自有的方法属性(类结构)自然被包含在了这个对应的实例上,...
  • 最近工作中涉及到获取同程火车票,大概描述为:将本地获取的发出城市,目的城市及出发时间按固定格式封装,调用接口获取可乘坐座席等级最高的火车票,接口返回...在获取具体某个座席时会用到Java反射,笔者觉得应...
  • 可以看出这是一个可变参数(Java SE 5引入),不过它的本质其实对应类型的数组,这里是Object[] args, 对于方法invoke(Object obj, Object… args),会在方法内部创建一个对象,Object[] args = new Object[n],n的...
  • java反射方法反射的基本操作方法

    千次阅读 2021-03-22 17:04:11
    本文接上文“java反射之获取类的信息方法(推荐)”,利用反射(invoke)来获取一个类中的方法来执行。1、定义一个类,包含三个名称相同,参数不同的方法class A{public void print(){System.out.println("Hello,World")...
  • Javainvoke方法

    千次阅读 2021-02-08 17:01:03
    如果读一些Java或者相关框架的源码,实际上一定会经常出现invoke方法的调用,在自己或者团队封装框架时,如果有时候弄得不好经常也会报invoke相关的错。 invoke方法是干什么的?有什么具体用途? 首先要了解invoke...
  • Java使用invoke反射调用方法导致@Value、@Autowired等注解失效发生背景产生原因代码演示解决方案完整代码文章思想来源 发生背景 开发过程中使用到invoke进行反射调用serviceImpl实现类的方法,在运行中发现采用反射...
  • Java反射中Method类invoke方法的用法

    千次阅读 2021-02-26 14:41:54
    import java.lang.reflect.Method;public class InvokeTester {public int add(int param1, int param2) {return param1 + param2;}public String echo(String mesg) {return "echo" + mesg;}public static void mai...
  • java反射机制描述及Method.invoke解释
  • } 运行结果: 通过反射调用普通方法 通过class对象获取Method对象 通过Method对象的invoke方法传入使用方法的对象和方法的参数从而调用方法 //通过反射操作对象 public class Test09 { public static void main...
  • 上篇文章中回顾了一下Java反射相关的基础内容。这一节我们来深入研究Method类中的invoke方法,探寻它的奥秘。 注:本篇文章的所有源码都基于OpenJDK 1.8。 引入 即使没有学过反射,大家也一定会见过invoke方法。...
  • 反射的作用 1)运行时构造类对象; 2)运行时调用对象方法。 3)运行时判断对象所属的类; ...4)运行时判断任意一个类所具有的成员变量和方法;...这个反射中还有两个方法有时候问到: @CallerSensitive ...
  • java反射invoke

    千次阅读 2018-07-11 19:51:54
    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为java语言的反射机制。Java反射...
  • JAVA反射机制 invoke()方法的介绍

    千次阅读 2018-05-10 17:50:41
    JAVA反射机制JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射...
  • 通过Java反射机制调用某个方法,并传入参数。 1.只传入一个参数hello world: import java.lang.reflect.Method; import java.util.Date; public class InvokeMethodTest {  public static void main(String[]...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 122,695
精华内容 49,078
关键字:

java反射invoke方法

java 订阅