在框架中经常会会用到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步:

检查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 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的值:

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的委托类设置为这个生成的代理对象。这么说可能有点绕,下面用一幅图表示这个过程。

总体来说,当调用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.委托模式可以解决一种方案的多种实现之间自由切换,而代理模式只能根据传入的被代理对象来实现功能。

method.invoke()s的更多相关文章

  1. java反射机制详解 及 Method.invoke解释

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...

  2. java反射机制 + Method.invoke解释 getMethod + 反射理解

    功能: 通过读取另一个Dll去创建一个控件(Form,Button,TextBox,DataGridView),然后对当中一些属性进行检查. 创建控件的大致流程是,Assembly->Modul ...

  3. (转)Java.lang.reflect.Method invoke方法 实例

    背景:今天在项目中用到Method 的invoke方法,但是并不理解,查完才知道,原来如此! import java.lang.reflect.Method; /** * Java.lang.refl ...

  4. method.invoke(...)反射点

    import java.lang.reflect.Method; import java.util.Arrays; /** * @Author: hoobey * @Description: * @D ...

  5. Java反射机制及Method.invoke详解

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...

  6. 【java】method.invoke(方法底层所属对象/null,new Object[]{实际参数})

    反射调方法时无论是静态/非静态,固定/可变参数,都有Object对象数组对参数进行包装. package com.tn.clas; import java.lang.reflect.Method; i ...

  7. java中Method.invoke方法参数解析

    通过发射的机制,可以通过invoke方法来调用类的函数.invoke函数的第一个参数是调用该方法的实例,如果该方法是静态方法,那么可以用null或者用类来代替,第二个参数是变长的,是调用该方法的参数. ...

  8. Java中Method.invoke方法,反射?

    正常来说,我们调用对象的方法是通过dot运算符来进行的,这里我们介绍另一种方法,有以下几个步骤:1,获取该类的Class Type:2,通过getMethod方法获取Method对象:3,通过调用in ...

  9. method.invoke()使用

    public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentExcep ...

随机推荐

  1. Git 版本还原命令

    转载:https://blog.csdn.net/yxlshk/article/details/79944535 1.需求场景: 在利用github实现多人协作开发项目的过程中,有时会出现错误提交的情 ...

  2. centos, ubuntu 上docker 安装

    参考中国官网,可以愉快的访问,各种系统,在线/离线部署都有说明.中文哦!! https://docs.docker-cn.com/engine/installation/linux/docker-ce ...

  3. mysql中data和datatime的区别

    1.显示格式的区别 Date显示格式:YYYY-MM-DD:DateTime显示格式:YYYY-MM-DD HH:mm:ss. 2.显示范围的区别 Date显示范围是1601-01-01 到 9999 ...

  4. python程序—封装案例

    需求: 1.房子有户型.总面积.家具名称列表 房子没有任何家具 2.家具有名字和占地面积,其中 席梦思(bed):4平米 衣柜(chest): 2平米 餐桌(table): 1.5平米 3.将以上3个 ...

  5. 做数据挖掘,就算发 20 几分的 CNS 子刊,也是垃圾!?--转载

    关于数据挖掘发表文章,我们知道很多人是看不上.瞧不起.嗤之以鼻的.大抵是因为这些人平时只发 CNS 主刊,所以才认为通过数据挖掘这种用「别人的数据」或者叫「干实验」来发文章是“「垃圾」,没有什么价值. ...

  6. 【MVC】ASP.NET MVC之数据验证

    前端传到后端数据的不可信任性,DRY("Don't Repeat Yourself") 设计原则.MVC3.0出了后端数据验证特性,鼓励你只定义一次功能或行为,然后在应用程序中各处 ...

  7. 一个可以配置阴影方向和颜色的类 CardView 控件 SCardView

    一.控件简述 今天给大家推荐一个控件 SCardView ,看名字就很容易才出来它其实就是一个 CardView .把它拿出来,是因为它解决了一些 CardView 无法实现的需求以及简化了 Card ...

  8. grep 从文件内容中查找

    grep -rin [查找目标] [查找范围] 例子:在当前目录下的文件内查找test字符串 grep -rin test ./

  9. Go语言学习之11 日志收集系统kafka库实战

    本节主要内容: 1. 日志收集系统设计2. 日志客户端开发 1. 项目背景    a. 每个系统都有日志,当系统出现问题时,需要通过日志解决问题    b. 当系统机器比较少时,登陆到服务器上查看即可 ...

  10. 金蝶K3常用数据表

    金蝶K3WISE常用数据表 K3Wise 14.2 清空密码update t_User set FSID=') F ", ,P T #8 *P!D &D 80!N &@ &l ...