首页 » 网站建设 » phpinvokemethod技巧_Java反射之Method的invoke方法实现

phpinvokemethod技巧_Java反射之Method的invoke方法实现

访客 2024-11-19 0

扫一扫用手机浏览

文章目录 [+]

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

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

phpinvokemethod技巧_Java反射之Method的invoke方法实现

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

phpinvokemethod技巧_Java反射之Method的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 {@Overridepublic 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 classat 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步:

检讨override,如果override为true,跳过检讨;否则连续;快速检讨,判断该方法的润色符modifiers是否为public,如果是跳过检讨;否则连续;详细检讨,通过方法的(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 volatileif (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的值:

熊猫追剧-在线视频网站-海量高清视频免费在线不雅观看

相关文章

介绍域名盗取之谜,防范之路与应对步骤

在互联网高速发展的今天,域名作为企业的网络标识,其重要性不言而喻。域名盗取事件频发,给企业带来了巨大的经济损失和信誉风险。本文将揭...

网站建设 2024-12-17 阅读0 评论0

介绍咖秀,如何玩转这款时尚社交平台

随着互联网技术的飞速发展,社交平台层出不穷,为广大用户提供了丰富多彩的社交体验。咖秀作为一款时尚、潮流的社交软件,凭借其独特的功能...

网站建设 2024-12-17 阅读0 评论0

介绍大数据底层架构,构建智能时代的基石

随着互联网技术的飞速发展,大数据已成为我国经济发展的重要驱动力。在智能时代,大数据底层架构作为支撑整个数据生态系统的基石,其重要性...

网站建设 2024-12-17 阅读0 评论0

介绍播客创作之路,从灵感迸发到受众共鸣

随着移动互联网的快速发展,播客作为一种新兴的传播媒介,逐渐走进大众视野。越来越多的人开始尝试创作播客,分享自己的见解与故事。如何从...

网站建设 2024-12-17 阅读0 评论0