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 ..."); } }