Call Methods at Runtime Using Java Reflection – 使用Java反思在运行时调用方法

最后修改: 2017年 5月 8日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this short article, we’ll take a quick look at how to invoke methods at runtime using the Java Reflection API.

在这篇短文中,我们将快速了解如何使用Java Reflection API在运行时调用方法

2. Getting Ready

2.做好准备

Let’s create a simple class which we’ll use for the examples that follow:

让我们创建一个简单的类,我们将在下面的例子中使用。

public class Operations {
    public double publicSum(int a, double b) {
        return a + b;
    }

    public static double publicStaticMultiply(float a, long b) {
        return a * b;
    }

    private boolean privateAnd(boolean a, boolean b) {
        return a && b;
    }

    protected int protectedMax(int a, int b) {
        return a > b ? a : b;
    }
}

3. Obtaining a Method Object

3.获得一个方法对象

Firstly, we need to get a Method object that reflects the method we want to invoke. The Class object, representing the type in which the method is defined, provides two ways of doing this.

首先,我们需要获得一个Method对象,反映我们想要调用的方法。代表方法所定义的类型的 Class对象提供了两种方法。

3.1. getMethod()

3.1.getMethod()

We can use getMethod() to find any public method of the class or any of its superclasses.

我们可以使用getMethod()来查找类或其任何超类的任何公共方法。

Basically, it receives the method name as the first argument, followed by the types of the method’s arguments:

基本上,它接收方法名称作为第一个参数,然后是方法参数的类型。

Method sumInstanceMethod
  = Operations.class.getMethod("publicSum", int.class, double.class);

Method multiplyStaticMethod
  = Operations.class.getMethod(
    "publicStaticMultiply", float.class, long.class);

3.2. getDeclaredMethod()

3.2.getDeclaredMethod()

We can use getDeclaredMethod() to get any kind of method. This includes public, protected, default access, and even private methods, but excludes inherited ones.

我们可以使用getDeclaredMethod()来获取任何种类的方法。这包括公共的、受保护的、默认访问的,甚至是私有的方法,但不包括继承的。

It receives the same parameters as getMethod():

它接收的参数与getMethod()相同。

Method andPrivateMethod
  = Operations.class.getDeclaredMethod(
    "privateAnd", boolean.class, boolean.class);
Method maxProtectedMethod
  = Operations.class.getDeclaredMethod("protectedMax", int.class, int.class);

4. Invoking Methods

4.调用方法

With the Method instance in place, we can now call invoke() to execute the underlying method and get the returned object.

有了Method实例,我们现在可以调用invoke()来执行底层方法并获得返回的对象。

4.1. Instance Methods

4.1.实例方法

To invoke an instance method, the first argument to invoke() must be an instance of Method that reflects the method being invoked:

要调用一个实例方法,invoke()的第一个参数必须是反映被调用方法的Method的一个实例。

@Test
public void givenObject_whenInvokePublicMethod_thenCorrect() {
    Method sumInstanceMethod
      = Operations.class.getMethod("publicSum", int.class, double.class);

    Operations operationsInstance = new Operations();
    Double result
      = (Double) sumInstanceMethod.invoke(operationsInstance, 1, 3);

    assertThat(result, equalTo(4.0));
}

4.2. Static Methods

4.2.静态方法

Since these methods don’t require an instance to be called, we can pass null as the first argument:

由于这些方法不需要调用一个实例,我们可以将null作为第一个参数传递。

@Test
public void givenObject_whenInvokeStaticMethod_thenCorrect() {
    Method multiplyStaticMethod
      = Operations.class.getDeclaredMethod(
        "publicStaticMultiply", float.class, long.class);

    Double result
      = (Double) multiplyStaticMethod.invoke(null, 3.5f, 2);

    assertThat(result, equalTo(7.0));
}

5. Method Accessibility

5.方法的可及性

By default, not all reflected methods are accessible. This means that the JVM enforces access control checks when invoking them.

默认情况下,并非所有被反射的方法都是可访问的这意味着JVM在调用这些方法时执行了访问控制检查。

For instance, if we try to call a private method outside its defining class or a protected method from outside a subclass or its class’ package, we’ll get an IllegalAccessException:

例如,如果我们试图在其定义的类之外调用一个私有方法,或者从子类或其类的包之外调用一个受保护的方法,我们会得到一个IllegalAccessException

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokePrivateMethod_thenFail() {
    Method andPrivateMethod
      = Operations.class.getDeclaredMethod(
        "privateAnd", boolean.class, boolean.class);

    Operations operationsInstance = new Operations();
    Boolean result
      = (Boolean) andPrivateMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

@Test(expected = IllegalAccessException.class)
public void givenObject_whenInvokeProtectedMethod_thenFail() {
    Method maxProtectedMethod
      = Operations.class.getDeclaredMethod(
        "protectedMax", int.class, int.class);

    Operations operationsInstance = new Operations();
    Integer result
      = (Integer) maxProtectedMethod.invoke(operationsInstance, 2, 4);
    
    assertThat(result, equalTo(4));
}

5.1. AccessibleObject#setAccesible

5.1.AccessibleObject#setAccesible

By calling setAccesible(true) on a reflected method object, the JVM suppresses the access control checks and allows us to invoke the method without throwing an exception:

通过在被反射的方法对象上调用setAccesible(true),JVM抑制了访问控制检查,并允许我们调用该方法而不抛出异常。

@Test
public void givenObject_whenInvokePrivateMethod_thenCorrect() throws Exception {
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    andPrivatedMethod.setAccessible(true);

    Operations operationsInstance = new Operations();
    Boolean result = (Boolean) andPrivatedMethod.invoke(operationsInstance, true, false);

    assertFalse(result);
}

5.2. AccessibleObject#canAccess

5.2.AccessibleObject#canAccess

Java 9 comes with a brand new way to check whether a caller can access a reflected method object.

Java 9提供了一种全新的方法来检查调用者是否可以访问反射的方法对象

For this purpose, it provides canAccess as a replacement for the deprecated method isAccessible​.

为此,它提供了canAccess作为被废弃的方法isAccessible的替代。

Let’s see it in action:

让我们看看它的行动。

@Test
public void givenObject_whenInvokePrivateMethod_thenCheckAccess() throws Exception {
    Operations operationsInstance = new Operations();
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);
 
    assertFalse(isAccessEnabled);
 }

We can use canAccess to check if the caller already has access to the reflected method before setting the accessible flag to true with setAccessible(true).

我们可以使用canAccess来检查调用者在用setAccessible(true)ccessible标志设置为true之前是否已经有了对被反射方法的访问。

5.3. AccessibleObject#trySetAccessible

5.3.AccessibleObject#trySetAccessible

trySetAccessible is another handy method that we can use to make a reflected object accessible.

trySetAccessible是另一个方便的方法,我们可以用它来使一个被反射的对象可被访问。

The good thing about this new method is that it returns false if the access cannot be enabled. However, the old method setAccessible(true) throws InaccessibleObjectException when it fails.

这个新方法的好处是,如果不能启用访问,它返回false。然而,旧方法setAccessible(true)失败时抛出InaccessibleObjectException

Let’s exemplify the use of the trySetAccessible method:

让我们举例说明trySetAccessible方法的使用。

@Test
public void givenObject_whenInvokePublicMethod_thenEnableAccess() throws Exception {
    Operations operationsInstance = new Operations();
    Method andPrivatedMethod = Operations.class.getDeclaredMethod("privateAnd", boolean.class, boolean.class);
    andPrivatedMethod.trySetAccessible();
    boolean isAccessEnabled = andPrivatedMethod.canAccess(operationsInstance);
        
    assertTrue(isAccessEnabled);
}

6. Conclusion

6.结论

In this quick article, we’ve seen how to call instance and static methods of a class at runtime through reflection. We also showed how to change the accessible flag on the reflected method objects to suppress Java access control checks when invoking private and protected methods.

在这篇快速文章中,我们已经看到了如何通过反射在运行时调用类的实例和静态方法。我们还展示了在调用私有和受保护的方法时,如何改变被反射的方法对象的可访问标志以抑制Java的访问控制检查。

As always, the example code can be found over on Github.

一如既往,示例代码可以在Github上找到over