views:

393

answers:

5

I would like to avoid reflection in an open source project I am developing. Here I have classes like the following.

public class PurchaseOrder {

   @Property
   private Customer customer;

   @Property
   private String name;
}

I scan for the @Property annotation to determine what I can set and get from the PurchaseOrder reflectively. There are many such classes all using java.lang.reflect.Field.get() and java.lang.reflect.Field.set().

Ideally I would like to generate for each property an invoker like the following.

public interface PropertyAccessor<S, V> {
   public void set(S source, V value);
   public V get(S source);
}

Now when I scan the class I can create a static inner class of PurchaseOrder like so.

static class customer_Field implements PropertyAccessor<PurchaseOrder, Customer> {
   public void set(PurchaseOrder order, Customer customer) {
      order.customer = customer;
   }  
   public Customer get(PurchaseOrder order) {
      return order.customer;
   }
}

With these I totally avoid the cost of reflection. I can now set and get from my instances with native performance. Can anyone tell me how I would do this. A code example would be great. I have searched the net for a good example but can find nothing like this. The ASM and Javasist examples are pretty poor also.

The key here is that I have an interface that I can pass around. So I can have various implementations, perhaps one with Java Reflection as a default, one with ASM, and one with Javassist?

Any help would be greatly appreciated.

+1  A: 

You could use Annotation Processors too, thus avoiding the complexity of bytecode manipulation. (see this article on javabeat)

Olivier Croisier
This is not event the point, what I want here is the removal of reflection to get and set values for the instance. Annotations and annotation processing have little to do with what I am asking.
ng
I was not speaking about using reflexion to look for annotations. I was talking about Annotation Processors. You didn't even check the link I provided...Pluggable Annotation Processors are like compiler plugins.They are run during the compilation phase, and allow you to look for annotations in the source code in order to produce new entities - most of the time configuration files, or new classes like the accessor classes you want to produce.Hence, there is no reflexion involved in the solution I propose : everything is generated and compiled.
Olivier Croisier
Here I am scanning the files at runtime, I do not even know they exist from the library. The goal is to dynamically determine which fields I am interested by looking for @Property, when I find them I can then generate an accessor dynamically to interact with the value.
ng
Ah, I see. You didn't state that it had to be at runtime.Generally speaking, Javassist is known to be easier to use than ASM, especially to generate such simple structure as accessors. But I cannot help you further with these tools, sorry.
Olivier Croisier
A: 

I am surprised reflection is so much slower. If you warm up the JVM it shouldn't be more than 5x slower than direct access. BTW A micro-benchmark can give mis-leading results because a simple getter/setter can be easily optimised away to nothing if it doesn't do real work.

Another way you can avoid reflection and byte code is to use the sun.misc.Unsafe class. It has to be handled with care and its not portable to all JVMs, but it is 2-3x faster than reflection. See my essence-rmi project for examples.

Another option is to generate code and compile it on the fly. You can use the Compiler API or a library like BeanShell.

Note: if you have a private field, it cannot be accessed from another class using byte code. This is a JVM restriction. Inner and nested classes avoid this by generating accessor methods for you like access$100 in the class with the private methods (you may have seen these in your call stack) However, it means you cannot add a class to access private fields without altering the original class.

Peter Lawrey
This is not an accurate answer at all, have you ever even looked at java.lang.Field.set()? Modification of the code is not a problem for me. There are a lot of ways to do this, using BeanShell seems like a really nasty way to do this. The compiler API would be way to slow for simple accessors.
ng
A: 

The goal is performance!

Yes, that is in very many cases the goal. But what you are doing now with the PropertyAccessor your performance goes downwards! Every time you want to get or set a property, you will have to create a new instance for customer_Field. Or you have to keep your instance. I don't see what the problem is with a simple getter or setter.

public class PurchaseOrder {

   @Property
   private Customer customer;

   @Property
   private String name;

   pulic void setCustomer(Customer c)
   {
       this.customer = c;
   }

   public Customer getCustomer()
   {
       return customer;
   }

   // The same for name
}

This is performance! Native code is maybe 14 times faster, but do you really need that fast? Java is great. Why? Because of it platform independence. And if you are going to make native stuff, the power of Java is gone. So and what is the difference between waiting one minute for doing everything what the programs needs to do and waiting 50 seconds. "Where is my 14 times faster?" You don't only need to get and set. You need to do something with all the data.

And I don't think it would be faster, because you are just getting and setting object instances and primitives. Native Java is created for:

  • methods which have to calculate something that would be really faster in machine code than with Java Runtime Environment (A lot of java.lang.Math methods, like sqrt(). They could program it in Java but it would be slower)
  • things Java can't do by himself, like: exit application, create sockets, write/read files , invoking other processes, etc... That isn't pure Java that is Native Machine code that that does.

So I hope I persuaded you and you will keep it by Java.

Martijn Courteaux
I don't think you really understand the question here. What I am requesting is 100% platform independent. Its Java Byte Code!! Implementing getters and setters defeats the whole idea, it has to be dynamic and at runtime. See JAXB for an example of such a use case.
ng
Performance of JAXB by the way is mostly not due to code generation. This is common misconception.There are faster tools that do not need bytecode generation either during compile or runtime. Jackson JSON processor, for example, currently just uses plain old reflection, yet being faster than JAXB.(some difference is due to XML vs JSON but most is not -- which can be seen when comparing 'raw' xml/json streaming parsing)
StaxMan
+2  A: 

ASM

Using ASMifierClassVisitor, you can see exactly what code you need to write to generate the inner classes:

ASMifierClassVisitor.main(new String[] { PurchaseOrder.customer_Field.class
    .getName() });

The rest is just determining what bits you need to parameterize in your generator code. Example output for PurchaseOrder$customer_Field which will become the file inject/PurchaseOrder$customer_Field.class:

public static byte[] dump () throws Exception {

  ClassWriter cw = new ClassWriter(0);
  FieldVisitor fv;
  MethodVisitor mv;
  AnnotationVisitor av0;

  cw.visit(V1_6, ACC_SUPER, "inject/PurchaseOrder$customer_Field",
      "Ljava/lang/Object;"+
      "Linject/PropertyAccessor<Linject/PurchaseOrder;Linject/Customer;>;", 
      "java/lang/Object",
      new String[] { "inject/PropertyAccessor" });
//etc

(I used "inject" as the package.)

You'll also have to create synthetic accessors using ASM's visitor classes:

{
mv = cw.visitMethod(ACC_STATIC + ACC_SYNTHETIC, "access$0", 
          "(Linject/PurchaseOrder;Linject/Customer;)V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitFieldInsn(PUTFIELD, "inject/PurchaseOrder",
          "customer", "Linject/Customer;");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_STATIC + ACC_SYNTHETIC, "access$1", 
          "(Linject/PurchaseOrder;)Linject/Customer;", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "inject/PurchaseOrder", "
          customer", "Linject/Customer;");
mv.visitInsn(ARETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}

See this project for an example of how to inject methods.


With these I totally avoid the cost of reflection.

Since this is all going to be done at runtime:

  • there is an up-front cost to this parsing and code generation
  • you'll need to discover and introspect these generated types somehow
McDowell
+1  A: 

An example using Javassist, however it does require that your properties have package level protection instead of be private

public class AccessorGenerator {

    private final ClassPool pool;

    public PropertyGenerator() {
        pool = new ClassPool();
        pool.appendSystemPath();
    }

    public Map<String, PropertyAccessor> createAccessors(Class<?> klazz) throws Exception {
        Field[] fields = klazz.getDeclaredFields();

        Map<String, PropertyAccessor> temp = new HashMap<String, PropertyAccessor>();
        for (Field field : fields) {
            PropertyAccessor accessor = createAccessor(klazz, field);
            temp.put(field.getName(), accessor);
        }

        return Collections.unmodifiableMap(temp);
    }

    private PropertyAccessor createAccessor(Class<?> klazz, Field field) throws Exception {
        final String classTemplate = "%s_%s_accessor";
        final String getTemplate = "public Object get(Object source) { return ((%s)source).%s; }";
        final String setTemplate = "public void set(Object dest, Object value) { return ((%s)dest).%s = (%s) value; }";

        final String getMethod = String.format(getTemplate, 
                                               klazz.getName(),
                                               field.getName());
        final String setMethod = String.format(setTemplate, 
                                               klazz.getName(), 
                                               field.getName(), 
                                               field.getType().getName());

        final String className = String.format(classTemplate, klazz.getName(), field.getName());

        CtClass ctClass = pool.makeClass(className);
        ctClass.addMethod(CtNewMethod.make(getMethod, ctClass));
        ctClass.addMethod(CtNewMethod.make(setMethod, ctClass));
        ctClass.setInterfaces(new CtClass[] { pool.get(PropertyAccessor.class.getName()) });
        Class<?> generated = ctClass.toClass();
        return (PropertyAccessor) generated.newInstance();
    }

    public static void main(String[] args) throws Exception {
        AccessorGenerator generator = new AccessorGenerator();

        Map<String, PropertyAccessor> accessorsByName = generator.createAccessors(PurchaseOrder.class);

        PurchaseOrder purchaseOrder = new PurchaseOrder("foo", new Customer());

        accessorsByName.get("name").set(purchaseOrder, "bar");
        String name = (String) accessorsByName.get("name").get(purchaseOrder);
        System.out.println(name);
    }
}
Michael Barker
So is this a static inner class of the original? If so then I could leave the access as private.
ng
For a static inner class that acesses private fields of the outer class, the javac compiler actually generates additional hidden (synthetic) methods in the outer class that get and set these fields. This way a private field is still only accessible from the owning class.
Jörn Horstmann
It seems strange then that a byte code generator needs to respect the modifiers on a field. Surely its just a case of load variable and store value? Why would modifiers even come in to the equation? I should be able to generate some code, that implements a know interface that can set what ever I want?
ng
Access modifiers are enforced by the jvm at the bytecode level, so they can't be circumvented by the compiler. Since inner classes where introduced after java 1.0, the access semantics weren't changed, instead the compiler generates additional package private accessor methods.
Jörn Horstmann
I don't think this is true, what about method.setAccessible(true), this does not change any bytecodes, is simply slips passed all the checks in the method to invoke the value. I think this is also possible with ASM or Javassist.
ng
You could actually try this out by creating two classes, class A has a public field, class B accesses this field. Then change the field to private and try to run the program with the old version of B and the new verion of A in the classpath. Now B will try to access a private field and you will get an IllegalAccessError or something similar.
Jörn Horstmann
@ng, Jom is correct. To make this work with private fields, it would be necessary to mix-in new accessor methods into the original class, or modify the projection level of the field declarations. Using reflection you are accessing the values from the object after the code has been through the verifier. The verifier will reject new byte code (e.g. that generated by javassist) that attempt to access private members directly.
Michael Barker