开发者

Aspect frameworks with better performance than AspectJ in this case?

开发者 https://www.devze.com 2023-02-16 09:42 出处:网络
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 firePropertyC

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消