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;", "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.
You could use Annotation Processors too, thus avoiding the complexity of bytecode manipulation. (see this article on javabeat).
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.
Jun 11 '10 at 13:05 1 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 Jun 11 '10 at 14:15 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. Jun 11 '10 at 14:24 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 Jun 11 '10 at 14:45.
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 createAccessors(Class klazz) throws Exception { Field fields = klazz. GetDeclaredFields(); Map temp = new HashMap(); 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 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); } }.
If so then I could leave the access as private. – ng. Jun 21 '10 at 12:33 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 Jun 21 '10 at 14:02 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.
Jun 21 '10 at 16:20 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 Jun 21 '10 at 18:49 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.
Jun 21 '107 at 12:36.
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.
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. Jun 15 '10 at 10:15.
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.
1 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.
Jun 15 '10 at 14:50 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 Jul 31 '10 at 4:57.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.