Aspect frameworks with better performance than AspectJ in this case?
I know AspectJ and use it successfully. One of our aspects adds Beanstyl开发者_C百科e Property Change support to classes by surrounding each setX method with the corresponding calls to a firePropertyChange() method.
I however noticed that now, for each setter, an inner class is created by AspectJ. I wondered if this is maybe inperformant (is it really?), and if it wouldn't be better to modify the generated in a way where the code is injected directly into the method functions.
I do not now about a AspectJ framework that allows this, and I know I wouldn't be able to define my aspects as comfortable as it is in AspectJ, but are there other Aspect frameworks that allow a more direct manipulation of the classes? Frameworks which are more performant than AspectJ in this case? Even if the programming overhead is higher?
Before trying to optimize this I would recommend profiling your existing code or analyzing the generated classes with a decompiler / disassembler to see if there are any obvious performance bottlenecks. Just because there are additional classes does not automatically make your code slow, the hotspot compiler is pretty clever and might be able to inline all of the generated code.
If you are still convinced you have a performance problem you might try the asm bytecode library. Although it is not really an aspect framework, it can be used to transform arbitrary class files using the visitor pattern. It is a low-level approach and requires some know-how of the java bytecode format. In fact, I implemented this some time ago as a learning exercise. Here is the relevant part of the ClassAdapter
implementation to give you an idea how it works.
@Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
if (instrument && name.equals("<init>")) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
// insert code in constructor to initialize instance field propertyChangeSupport
...
} else if (instrument && isPublicSetter(access, name, desc)) {
String propertyName = Introspector.decapitalize(name.substring(3));
String getter = "get" + name.substring(3);
Type type = Type.getArgumentTypes(desc)[0];
String arg = type.getDescriptor();
// rename method by prepending an underscore and make it private
MethodVisitor orig = super.visitMethod(access & ~ACC_PUBLIC | ACC_PRIVATE | ACC_SYNTHETIC, "_" + name, desc, signature, exceptions);
// create a wrapper method replacing the original one
MethodVisitor wrap = super.visitMethod(access, name, desc, signature, exceptions);
wrap.visitCode();
wrap.visitVarInsn(ALOAD, 0);
wrap.visitFieldInsn(GETFIELD, classname, "_propertyChangeSupport", "Ljava/beans/PropertyChangeSupport;");
// Stack: _propertyChangeSupport
wrap.visitLdcInsn(propertyName);
// stack: _propertyChangeSupport, propertyName
wrap.visitVarInsn(ALOAD, 0);
// stack: _propertyChangeSupport, propertyName, this
wrap.visitMethodInsn(INVOKEVIRTUAL, classname, getter, "()" + arg);
generateAutoBoxIfNeccessary(wrap, arg);
// stack: _propertyChangeSupport, propertyName, oldValue
wrap.visitVarInsn(type.getOpcode(ILOAD), 1);
generateAutoBoxIfNeccessary(wrap, arg);
// stack: _propertyChangeSupport, propertyName, oldValue, newValue
wrap.visitVarInsn(ALOAD, 0);
wrap.visitVarInsn(type.getOpcode(ILOAD), 1);
// stack: _propertyChangeSupport, property, oldValue, newValue, this, newValue
// invoke original setter
wrap.visitMethodInsn(INVOKEVIRTUAL, classname, "_" + name, desc);
// stack: _propertyChangeSupport, property, oldValue, newValue
wrap.visitMethodInsn(INVOKEVIRTUAL, "java/beans/PropertyChangeSupport", "firePropertyChange", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V");
wrap.visitInsn(RETURN);
wrap.visitMaxs(6, 2);
wrap.visitEnd();
return orig;
} else {
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
You would also need some code to integrate this transformation in your build script or implement an java agent / custom class loader to do it at load-time.
If you are interested I can try to put the complete code online.
Edit: I polished and refactored the code and put it up at github. Its packaged as a maven plugin, the transformation itself is contained in the class net.jhorstmann.propertychangesupport.Transform
. The code changed a bit from the above since I'm now using an AdviceAdapter
to wrap the setter methods.
If you are doing a benchmark, please post your result somewhere. Also, if you have further questions, feel free to ask.
精彩评论