ClassWriter usage in asm, custom ClassVisitor and enhancer usage instance of cglib

ASM is a popular class library used to read script code in Java. It is used to analyze and convert code based on bytecode layer. In the process of reading and writing, custom logic can be added to enhance or modify the original compiled bytecode. For example, CGLIB uses it to implement dynamic proxy. ASM is designed to generate and transform Java classes at runtime, including offline processing. ASM is short and fast, so as to avoid the impact on the program speed when dynamically generating bytecode or converting at runtime. Because of its small size, ASM can be used in many memory limited environments.

Use of ClassWriter

ClassVisitor is used to generate asm and change bytecode. ClassVisitor is a
A framework for accessing bytecode. Its creation and modification of bytecode are mainly represented by its internal ClassVisitor
ClassWriter: ClassWriter is an implementation class of ClassVisitor

ClassWriter provides a class visit method to write classes. Use the visit method to start writing and the visitendmethod to end writing.

Specifically, this kind of visit method can add properties and methods to the classes that need to be modified. For example, visitMethod can add methods to a class, and visitAttribute can add attributes to a class.

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
  public static void main(String[] args) throws IOException {
        ClassWriter cs = new ClassWriter(0);
        //Determine the synchronization information of the class through vist. java version number class modifier class permission naming
        cs.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,"HZYziyi",null, "java/lang/Object",null);
        //Constructor
        MethodVisitor mv = cs.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
        // Define code method
        MethodVisitor methodVisitor = cs.visitMethod(Opcodes.ACC_PUBLIC, "code", "()V", null, null);
        methodVisitor.visitCode();
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        methodVisitor.visitLdcInsn("I'm a Ha ha ha Duck,Just Coding.....");
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 2);
        methodVisitor.visitEnd();
        cs.visitEnd();
        // Make classWriter class complete
        // Convert the classWriter into a byte array and write it to the file
        byte[] data = cs.toByteArray();
        File file = new File("./HZYziyi.class");
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(data);
        fout.close();
    }

At the end of the run, you can see that a hzyziyi Class file, you can see the contents:

Customize a ClassVisitor

Write a class to inherit ClassVisitor

public class ClassChangeAdapter extends ClassVisitor {

    public ClassChangeAdapter(ClassVisitor classVisitor) {
        super(ASM4, classVisitor);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        // If the main method is currently accessed, it will be removed
        if(name.equals("main")) {
            return null;
        }
        MethodVisitor mv =super.visitMethod(access, name,
                desc, signature, exceptions);
        MethodChangeAdaper methodChangeAdaper = new MethodChangeAdaper(ASM4, mv, access, name, desc);
        return methodChangeAdaper;
    }

}

The custom methodchangeadapter class inherits the AdviceAdapter

public class MethodChangeAdaper extends AdviceAdapter {
    /**
     * Creates a new {@link AdviceAdapter}.
     *
     * @param api    the ASM API version implemented by this visitor. Must be one
     *               of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
     * @param mv     the method visitor to which this adapter delegates calls.
     * @param access the method's access flags (see {@link Opcodes}).
     * @param name   the method's name.
     * @param desc   the method's descriptor (see {@link Type Type}).
     */
    protected MethodChangeAdaper(int api, MethodVisitor mv, int access, String name, String desc) {
        super(api, mv, access, name, desc);
    }

    @Override
    public void visitCode() {
        super.visitCode();

        //Call static method
        mv.visitMethodInsn(INVOKESTATIC, "com/tuling/cglib/proxy/DaoAnotherProxy", "before", "()V", false);

        // Create a new local variable table index
        // Push int type 2 to the top of the operand stack
        mv.visitInsn(ICONST_2);
        ///Create a new local variable table index
        int newLocal =  super.newLocal(jdk.internal.org.objectweb.asm.Type.INT_TYPE);
        mv.visitIntInsn(ISTORE, newLocal);
        mv.visitIincInsn(newLocal, 1);
        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv.visitIntInsn(ILOAD, newLocal);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
           mv.visitMethodInsn(INVOKESTATIC, "com/tuling/cglib/proxy/DaoAnotherProxy", "after", "()V", false);
    }
}

In MV visitMethodInsn(INVOKESTATIC, “com/tuling/cglib/proxy/DaoAnotherProxy”, “before”, “()V”, false);
In this sentence, you call your own before method of the DaoAnotherProxy class under the package.
DaoAnotherProxy class:

among public class DaoAnotherProxy {

    public static void before(){
        System.out.println("Before advice !");
    }


    public static void after(){
        System.out.println("Post notification!");
    }
}

Main category:

public class ClassChangeMethodTest {
    public static void main(String[] args) throws Exception {
        ClassWriter cw = new ClassWriter(COMPUTE_MAXS);
        ClassReader cr = new ClassReader(TestmyQuestion.class.getName());

        ClassChangeAdapter adapter=new ClassChangeAdapter(cw);

        cr.accept(adapter,ClassReader.SKIP_FRAMES);

        byte[] bytes = cw.toByteArray();

        File file = new File("./TestmyQuestion.class");
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(bytes);
        fout.close();



        MyClassLoader classLoader=new MyClassLoader();
        Class loadedClass = classLoader.defineClass(TestmyQuestion.class.getName(), bytes);

        loadedClass.newInstance();
        //Create a new instance of the Class represented by this Class object. This Class is instantiated as a new expression with an empty parameter list. If the Class has not been initialized, initialize it
    }
}

Where TestmyQuestion class:

public class TestmyQuestion {
    public static void main(String[] args) {
        int i=2;
        i++;
        System.out.println(i);
    }
}

After the above program is run, you can see that a class file testmyquestion is generated in the directory class:

import com.tuling.cglib.proxy.DaoAnotherProxy;

public class TestmyQuestion {
    public TestmyQuestion() {
        DaoAnotherProxy.before();
        byte var1 = 2;
        int var2 = var1 + 1;
        System.out.println(var2);
        DaoAnotherProxy.after();
        super();
    }
}

This is the class file generated by asm
Output:

Use of enhancer

Custom MethodInterceptor:

/**
 * Create a Dao proxy
 */
public class DaoProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("begin intercept");
        //The invokeSuper method calls the method of the target class
        proxy.invokeSuper(obj, args);
        System.out.println("end intercept");
        return obj;
    }
}

Custom CallbackFilter

import net.sf.cglib.proxy.CallbackFilter;

import java.lang.reflect.Method;

/**
 * Returns a numeric representation of the order
 */
public class DaoFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        System.out.println("accept");
        if("select".equalsIgnoreCase(method.getName())) {
            return 0;
        } else if ("delete".equalsIgnoreCase(method.getName())) {
            return 1;
        }
        return 2;
    }
}

Interface and implementation class:

public interface IDao {
    public void select();

    public void insert();

}

public class Dao implements IDao{



    @Override
    public void select() {
        System.out.println("select 1 from dual:");
        insert();
    }
    @Override
    public void insert() {
        System.out.println("insert into ...");
    }


}

Main program:

public class Client {
    public static void main(String[] args) {


        //Save agent class to local disk
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
        //Instantiation enhancer
        Enhancer enhancer = new Enhancer();
        //Set the target class that needs proxy
        enhancer.setSuperclass(Dao.class);
        //Set the implementation class of intercepting object callback
        enhancer.setCallback(new DaoProxy());
        //Use create Object to generate proxy classes and return instances
        Dao dao = (Dao) enhancer.create();
        //select has high priority. Use DaoProxy
        dao.select();
        //Cannot delegate a method modified by final
        //dao.delete();
       // dao.insert();

    }
}

Run the main program to see the output:

Generated proxy class file:

public class Dao implements IDao {
    public Dao() {
    }

    public void select() {
        DaoAnotherProxy.before();
        System.out.println("select 1 from dual");
    }

    public void insert() {
        System.out.println("insert into ...");
    }

    public final void delete() {
        System.out.println("delete from ...");
    }
}

Keywords: Java jar

Added by R4000 on Fri, 28 Jan 2022 00:17:54 +0200