简体   繁体   中英

What is the easiest way to find and run private annotated method?

The following example runs MyClass#myMethod() only if it is public. It does not run it if it is private.

How to run even if private?

import org.apache.commons.lang3.reflect.MethodUtils;

import javax.annotation.PostConstruct;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * Created by dims on 13.10.2016.
 */
public class CallPrivateMethodTest {

   @Retention(RUNTIME)
   @Target(METHOD)
   public @interface MyAnnotation {
   }

   public static class MyClass {

      @MyAnnotation
      private void myMethod() {
         System.out.println("myMethod() ran");
      }
   }

   public static void main(String[] args) {

      MyClass myObject = new MyClass();

      List<Method> methods = MethodUtils.getMethodsListWithAnnotation(myObject.getClass(), MyAnnotation.class);


      for(int i=0; i<methods.size(); ++i) {
         try {
            methods.get(i).invoke(myObject);
         } catch (IllegalAccessException e) {
            e.printStackTrace();
         } catch (InvocationTargetException e) {
            e.printStackTrace();
         }
      }
   }
}

You have to call setAccessible(true) on your method.

See the Javadoc of AccessibleObject , which in turn is a supertype of Method.

In your example:

methods.get(i).setAccessible(true);
methods.get(i).invoke(myObject);

EDIT: As GhostCat pointed out in his answer, not all reflection calls return private methods, too. It seems that MethodUtils#getMethodsListWithAnnotation in fact does not return them.

To solve this problem, you'd have to fetch those methods by yourself:

MyClass myObject = new MyClass ();

Method[] allMethods = myObject.getClass ().getDeclaredMethods ();
List<Method> annotatedMethods = Arrays.stream (allMethods)
                                      .filter (m -> m.getAnnotation (MyAnnotation.class) != null)
                                      .collect (Collectors.toList ());

for (Method method: annotatedMethods) {
  try {
    method.setAccessible (true);
    method.invoke (myObject);
  } catch (IllegalAccessException e) {
    e.printStackTrace ();
  } catch (InvocationTargetException e) {
    e.printStackTrace ();
  }
}

Basically, you need to run a private method from the class itself. Maybe you should rethink your code ? Otherwise, Alex's answer will do the trick.

More informations on access levels : https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

And if you want to use Reflection, you can find even more infos about the behavior here : Java reflection - impact of setAccessible(true)

Actually, there can be two reasons why your code doesn't work:

  1. That private method shows up in that list; but before calling it, you have to make it accessible (you already got an answer for that)
  2. Depending on the implementation of that Util class you are using, maybe getMethodsListWithAnnotation returns a list that simply doesn't include any private methods? ( you see, the mechanism in java reflection to get a hold on private methods is different than for public methods. thus it wouldnt surprise me if that util class is making the same distinction).

And in any case: private methods are private for a reason. They represent internal implementation details. They are subject to be changed all the time .

In that sense: your real problem is that you think there is a valid reason to invoke a private method using reflection. You should work on that first . If you really think that method should be annotated; and be called via reflection; then at least make it public .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM