Is there a general “backend” library for Java reflection

两盒软妹~` 提交于 2019-12-18 11:54:08

问题


I'm currently working with a specialized, interpreted, programming language implemented in Java. As a very small part of the language, I'd like to add the ability to make calls into Java. Before I dive into all of the nitty-gritty of reflection, I was wondering if anyone knew of a general library for doing the "backend" part of invoking Java code reflectively.

That is, I parse a string (I define the grammar) into some data structure that represents a Java method call (or constructor, or field access) and then pass that data structure to this library that invokes the call and returns the result. In particular, I'd like it to already handle all the edge cases that I don't want to figure out:

  • Automagically pick the right method based on the types of arguments (like an intelligent Class.getDeclaredMethod())
  • Handle distinction between arrays and normal object references
  • etc

I've spent a little time looking at the implementations of dynamic languages on the JVM, but these are generally much more complicated than I'm looking for, or highly optimized for the particular language.

Another option is to convert my grammar into a string in some dynamic language and invoke it with Rhino or something, but that's a little more overhead than I'm looking for.

Thanks!


回答1:


Just a comment to your own answer; actually beanutils has support for getting "a close match" given a set of parameters. See getMatchingAccessibleMethod()

BeanUtils is really powerful and has lots of utility methods for inspecting classes. The same support is naturally available for constructors.




回答2:


Try the FEST Reflection module. It's a fluent way to do Java reflection. For example:

 String name = method("get").withReturnType(String.class)
                         .withParameterTypes(int.class)
                         .in(names)
                         .invoke(8);



回答3:


If you're looking for simplicity, I have created a simple library called jOOR in order to facilitate access to the reflection API in Java. It supports the most essential actions without building up a huge API. Here's an example of what jOOR code looks like:

String world = 
on("java.lang.String") // Like Class.forName()
.create("Hello World") // Call the most specific matching constructor
.call("substring", 6)  // Call the most specific matching substring() method
.call("toString")      // Call toString()
.get()                 // Get the wrapped object, in this case a String



回答4:


Take a look at Java's scripting support; I believe it will help you tackle your problem.




回答5:


Have a look at Apache Commons BeanUtils




回答6:


I would strongly consider also having a look at springs ReflectionUtils class. Very powerful reflection handling.




回答7:


TO raise this from the dead:

invoke(Object object, String methodName, Object[] args) 

Apache Commons lang has exactly that method. MethodUtils#invoke




回答8:


I have started to create a library com.lexicalscope.fluent-reflection:fluent-reflection which integrates with hamcrest and lambdaj

You can write code like this; which calls all the post construct annotated methods in a class:

forEach(
   object(subject).methods(annotatedWith(PostConstruct.class)),
   ReflectedMethod.class).call();

Blog post here: http://www.lexicalscope.com/blog/category/software-projects/fluent-reflection/

Documentation here: http://fluent-reflection.lexicalscope.com/

You can get it from maven central here: http://repo1.maven.org/maven2/com/lexicalscope/fluent-reflection/fluent-reflection/

It has some basic features missing at the moment, like access to fields, but it works for methods. It will probably take a while to get to a really feature stable point (like a year or two), as I am only working on it occasionally. But it is developed to quite a high quality standard (I hope) and it is opensource so you can basically use it as it is now if it has all the features you need (you just might have to adjust your code a bit if you want to use newer versions that are released). I am using it in some production code at them moment.

It is designed to be quite extensible, so you can plugin in strategies to find the methods you want in a loosely coupled (compositional) style. So if it doesn't have the exact method lookup strategy you want, hopefully it is easy to add it.




回答9:


I ended up going with Alex's suggestion. BeanUtils helps a lot for beans, but I don't want to work solely with Beans. FEST looks really cool and I've bookmarked it for further study, but like BeanUtils, it doesn't appear to solve what I consider to be the difficult problem here. Namely, given a method name and list of arguments, pick the method that best "fits" the arguments. If a method takes a float and I have a double, it should be smart enough to not reject that method because the signature doesn't match exactly.

Obviously, scripting languages built on the JVM solve this problem, but in a much more complicated way than I need due to language-specific optimizations. So, since this is a minor and experimental feature, I've chosen an expeditious solution using the scripting engine support (JavaScript, in particular) in Java 1.6. Here's the basic idea:

private ScriptEngine engine = ... initialize with JavaScript engine ...

private Object invoke(Object object, String methodName, Object[] args) 
   throws RhsFunctionException
{
   // build up "o.method(arg0, arg1, arg2, ...)"
   StringBuilder exp = new StringBuilder("o." + methodName);
   engine.put("o", object);
   buildArgs(arguments, exp);

   try {
      return engine.eval(exp.toString());
   }
   catch (ScriptException e) {
      throw new RhsFunctionException(e.getMessage(), e);
   }
}

private void buildArgs(Object[] args, StringBuilder exp)
{
   // Use bindings to avoid having to escape arguments
   exp.append('(');
   int i = 0;
   for(Symbol arg : args) {
         String argName = "arg" + i;
         engine.put(argName, arg);
         if(i != 0) {
            exp.append(',');
         }
         exp.append(argName);
         ++i;
   }
   exp.append(')');
}

There's obviously a bit more to it, but this is the basic idea. I don't really like building up a string and evaluating it, but by using the bindings suggested by Alex, I avoid most of the pitfalls around escaping. Furthermore, I have a clean, simple interface that I can swap out with a "real" implementation if it proves necessary.

Any feedback or alternate solutions are more than welcome.




回答10:


I wrote and open-sourced this code after reading this thread, maybe you can find it useful.

https://github.com/yclian/Reflects.java/blob/master/src/test/java/my/jug/reflects/ReflectsTest.java

It's inspired by Guava, so you can use Predicate to filter the methods you like.



来源:https://stackoverflow.com/questions/364523/is-there-a-general-backend-library-for-java-reflection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!