In the last article, we analyzed the class files generated by Cglib, and we have probably seen how cglib implements dynamic proxies

Cglib dynamic proxy implementation principle

Today we will continue to implement cglib dynamic proxies using ASM

First we define a few classes and interfaces

Enhancer

  • Provides the method setSuperClass to set the proxy base class
  • Provides a method to set the callback type, setMethodInterceptor
  • Provides the method create to generate dynamic instances
package simple;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Enhancer {
    privateClass<? > superClass;private MethodInterceptor methodInterceptor;

    /** * Sets the parent class **@param superClass
     */
    public void setSuperClass(finalClass<? > superClass) {
        this.superClass = superClass;
    }

    /** * Sets the callback method instance **@param methodInterceptor
     */
    public void setMethodInterceptor(final MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    /** * Create dynamic instance object **@return* /
    public Object create(a) throws IOException {
        if (methodInterceptor == null) {
            try {
                return superClass.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        String className = "$ASMProxy";
        byte[] codeBytes = EnhancerFactory.generate(className, superClass);
        // Use custom class loaders to load bytecode
        ASMClassLoader asmClassLoader = new ASMClassLoader();
        asmClassLoader.add(className, codeBytes);
        try{ Class<? > aClass = asmClassLoader.loadClass(className); Constructor<? > constructor = aClass.getConstructor(MethodInterceptor.class);return constructor.newInstance(methodInterceptor);
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null; }}Copy the code

MethodInterceptor

The callback type interface MethodInterceptor defines the interface method Intercept

package simple;

import java.lang.reflect.Method;

public interface MethodInterceptor {
    Object intercept(Object obj, Method method, Object[] args, Method proxy) throws Throwable;
}
Copy the code
  • Obj: generated proxy class instance
  • Method: the method of the base class
  • Args: Array of arguments to call a method
  • Proxy: Proxy method through which the original method can be invoked

Use the generated proxy class:

package simple.test;

import simple.Enhancer;

public class Main {
    public static void main(String[] args) throws Throwable {
        Enhancer enhancer = new Enhancer();
        // Set the class to be proxied
        enhancer.setSuperClass(UserService.class);
        // Set the callback type, which handles the proxy logic
        enhancer.setMethodInterceptor(new UserMethodInterceptor());
        // Generate the proxy class instance
        UserService service = (UserService) enhancer.create();
        
        System.out.println(service.getClass().getName());
        System.out.println(service.login("admin"."admin"));
        System.out.println(service.login("admin"."admin1")); }}Copy the code

Let’s start by comparing the base class with the resulting proxy class

  • The base class for the proxy is required
package simple.test;

public class UserService {
    public boolean login(String username, String password) throws Throwable {
        return "admin".equals(username) && "admin".equals(password); }}Copy the code
  • Generated proxy classes
import java.lang.reflect.Method;
import simple.MethodInterceptor;
import simple.test.UserService;

public class $ASMProxy extends UserService {
    private MethodInterceptor methodInterceptor;
    private static Method _METHOD_login0 = Class.forName("simple.test.UserService").getMethod("login", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
    private static Method _METHOD_ASM_login0 = Class.forName("$ASMProxy").getMethod("_asm_login_0", Class.forName("java.lang.String"), Class.forName("java.lang.String"));

    public $ASMProxy(MethodInterceptor var1) {
        this.methodInterceptor = var1;
    }

    public boolean _asm_login_0(String var1, String var2) throws Throwable {
        return super.login(var1, var2);
    }

    public boolean login(String var1, String var2) throws Exception {
        return (Boolean)this.methodInterceptor.intercept(this, _METHOD_login0, newObject[]{var1, var2}, _METHOD_ASM_login0); }}Copy the code

The proxy class does:

  • Provides a parameter construct with a MethodInterceptor parameter type
  • Static fields, methods that store the original method and the proxy
  • Generates the method login, which calls the Intercept method on MethodInterceptor
  • Generate the staging method _ASM_logIN_0 for invoking the original method logic login

Now that you know what the generated proxy class looks like, use the ASM framework to generate the proxy class:

Go back to the Enhancer class and implement the logic that create() generates the proxy class

package simple;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Enhancer {
    privateClass<? > superClass;private MethodInterceptor methodInterceptor;

    /** * Sets the parent class **@param superClass
     */
    public void setSuperClass(finalClass<? > superClass) {
        this.superClass = superClass;
    }

    /** * Sets the callback method instance **@param methodInterceptor
     */
    public void setMethodInterceptor(final MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    /** * Create dynamic instance object **@return* /
    public Object create(a) throws IOException {
        if (methodInterceptor == null) {
            try {
                return superClass.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        String className = "$ASMProxy";
        // Generate proxy class byte arrays
        byte[] codeBytes = EnhancerFactory.generate(className, superClass);
        // Use custom class loaders to load bytecode
        ASMClassLoader asmClassLoader = new ASMClassLoader();
        asmClassLoader.add(className, codeBytes);
        try{ Class<? > aClass = asmClassLoader.loadClass(className); Constructor<? > constructor = aClass.getConstructor(MethodInterceptor.class);return constructor.newInstance(methodInterceptor);
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null; }}Copy the code

Create () here generates the proxy class byte array by calling enhancerFactory.generate (className, superClass).

To implement EnhancerFactory

EnhancerFactory

In general, there are four steps:

  • Implement the

    method
  • Adding static fields
  • Implement the < Clinit > method
  • Generate base class methods and staging methods
package simple;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class EnhancerFactory {
    public static byte[] generate(String proxyClassName, Class<? > superClass) { ClassWriter cw =new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        // Version number, access flag, class name...
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, proxyClassName, null, Type.getInternalName(superClass), null);
        // <init>
        createInit(cw, superClass, proxyClassName);
        // Static field
        addStaticFields(cw, superClass);
        // <clinit>
        addClinit(cw, superClass, proxyClassName);
        // The implementation method
        addSuperMethodImpl(cw, superClass, proxyClassName);
        cw.visitEnd();
        returncw.toByteArray(); }}Copy the code

Implement the <init> method

Generates a MethodInterceptor constructor with parameters of type MethodInterceptor

public $ASMProxy(MethodInterceptor var1) {
    this.methodInterceptor = var1;
}
Copy the code
private static void createInit(ClassWriter cw, Class
        superClass, String proxyClassName) {
    cw.visitField(Opcodes.ACC_PRIVATE, "methodInterceptor", Type.getDescriptor(MethodInterceptor.class), null.null);
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>"."(" + Type.getDescriptor(MethodInterceptor.class) + ")V".null.null);
    mv.visitCode();
    // push this onto the stack
    mv.visitVarInsn(Opcodes.ALOAD, 0);
    // super()
    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(superClass), "<init>"."()V".false);
    // Push this argument to the stack
    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitVarInsn(Opcodes.ALOAD, 1);
    // Assign field
    mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassName.replace('. '.'/'), "methodInterceptor", Type.getDescriptor(MethodInterceptor.class));
    / / return
    mv.visitInsn(Opcodes.RETURN);
    mv.visitMaxs(2.2);
    mv.visitEnd();
}
Copy the code

Adding static fields

Generate static fields to store method calls

private static void addStaticFields(ClassWriter cw, Class
        superClass) {
    Method[] methods = getMethods(superClass);
    for (int i = 0; i < methods.length; i++) {
        String fieldName = "_METHOD_" + methods[i].getName() + i;
        String asmFieldName = "_METHOD_ASM_" + methods[i].getName() + i;
        cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, fieldName, Type.getDescriptor(Method.class), null.null);
        cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, asmFieldName, Type.getDescriptor(Method.class), null.null); }}private static final List<String> FILTER_METHOD_NAMES = newArrayList();

private static List<String> newArrayList(a) {
    List<String> list = new ArrayList<>();
    list.add("wait");
    list.add("equals");
    list.add("toString");
    list.add("hashCode");
    list.add("getClass");
    list.add("notify");
    list.add("notifyAll");
    return list;
}
// Filtering does not require proxy methods
private staticMethod[] getMethods(Class<? > superClass) {returnArrays.stream(superClass.getMethods()).filter(it -> ! FILTER_METHOD_NAMES.contains(it.getName()) && it.getModifiers() ! = Modifier.FINAL).toArray(Method[]::new);
}
Copy the code

Implement the < Clinit > method

Assign values to generated static fields

private static void addClinit(ClassWriter cw, Class
        superClass, String proxyClassName) {
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>"."()V".null.null);
        mv.visitCode();
        Method[] methods = getMethods(superClass);
        for (int i = 0; i < methods.length; i++) {
            generateMethod(superClass, proxyClassName, mv, methods[i], i);
            generateASMMethod(superClass, proxyClassName, mv, methods[i], i);
        }
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(2.2);
        mv.visitEnd();
    }

    private static void generateMethod(Class<? > superClass, String proxyClassName, MethodVisitor mv, Method method,int i) {
        String fieldName = "_METHOD_" + method.getName() + i;
        mv.visitLdcInsn(superClass.getName());
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Class.class), "forName"."(Ljava/lang/String;) Ljava/lang/Class;".false);
        mv.visitLdcInsn(method.getName());
        if (method.getParameterCount() == 0) {
            mv.visitInsn(Opcodes.ACONST_NULL);
        } else {
            switch (method.getParameterCount()) {
                case 1:
                    mv.visitInsn(Opcodes.ICONST_1);
                    break;
                case 2:
                    mv.visitInsn(Opcodes.ICONST_2);
                    break;
                case 3:
                    mv.visitInsn(Opcodes.ICONST_3);
                    break;
                default:
                    mv.visitVarInsn(Opcodes.BIPUSH, method.getParameterCount());
                    break;
            }
            mv.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Class.class));
            for (int paramIndex = 0; paramIndex < method.getParameterTypes().length; paramIndex++) { Class<? > parameter = method.getParameterTypes()[paramIndex]; mv.visitInsn(Opcodes.DUP);switch (paramIndex) {
                    case 0:
                        mv.visitInsn(Opcodes.ICONST_0);
                        break;
                    case 1:
                        mv.visitInsn(Opcodes.ICONST_1);
                        break;
                    case 2:
                        mv.visitInsn(Opcodes.ICONST_2);
                        break;
                    case 3:
                        mv.visitInsn(Opcodes.ICONST_3);
                        break;
                    default:
                        mv.visitVarInsn(Opcodes.BIPUSH, paramIndex);
                        break;
                }
                mv.visitLdcInsn(parameter.getName());
                mv.visitMethodInsn(
                        Opcodes.INVOKESTATIC, Type.getInternalName(Class.class),
                        "forName"."(Ljava/lang/String;) Ljava/lang/Class;".false
                );
                mv.visitInsn(Opcodes.AASTORE);
            }
        }

        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Class.class), "getMethod"."(Ljava/lang/String; [Ljava/lang/Class;)Ljava/lang/reflect/Method;".false);
        mv.visitFieldInsn(Opcodes.PUTSTATIC, proxyClassName, fieldName, Type.getDescriptor(Method.class));
    }

    private static void generateASMMethod(Class<? > superClass, String proxyClassName, MethodVisitor mv, Method method1,int i) {
        Method method = method1;
        String asmFieldName = "_METHOD_ASM_" + method1.getName() + i;
        mv.visitLdcInsn(proxyClassName);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Class.class), "forName"."(Ljava/lang/String;) Ljava/lang/Class;".false);
        mv.visitLdcInsn("_asm_" + method.getName() + "_" + i);
        if (method.getParameterCount() == 0) {
            mv.visitInsn(Opcodes.ACONST_NULL);
        } else {
            switch (method.getParameterCount()) {

                case 1:
                    mv.visitInsn(Opcodes.ICONST_1);
                    break;
                case 2:
                    mv.visitInsn(Opcodes.ICONST_2);
                    break;
                case 3:
                    mv.visitInsn(Opcodes.ICONST_3);
                    break;
                default:
                    mv.visitVarInsn(Opcodes.BIPUSH, method.getParameterCount());
                    break;
            }
            mv.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Class.class));
            for (int paramIndex = 0; paramIndex < method.getParameterTypes().length; paramIndex++) { Class<? > parameter = method.getParameterTypes()[paramIndex]; mv.visitInsn(Opcodes.DUP);switch (paramIndex) {
                    case 0:
                        mv.visitInsn(Opcodes.ICONST_0);
                        break;
                    case 1:
                        mv.visitInsn(Opcodes.ICONST_1);
                        break;
                    case 2:
                        mv.visitInsn(Opcodes.ICONST_2);
                        break;
                    case 3:
                        mv.visitInsn(Opcodes.ICONST_3);
                        break;
                    default:
                        mv.visitVarInsn(Opcodes.BIPUSH, paramIndex);
                        break;
                }
                mv.visitLdcInsn(parameter.getName());
                mv.visitMethodInsn(
                        Opcodes.INVOKESTATIC, Type.getInternalName(Class.class),
                        "forName"."(Ljava/lang/String;) Ljava/lang/Class;".false
                );
                mv.visitInsn(Opcodes.AASTORE);
            }
        }


        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Class.class), "getMethod"."(Ljava/lang/String; [Ljava/lang/Class;)Ljava/lang/reflect/Method;".false);
        
        mv.visitFieldInsn(Opcodes.PUTSTATIC, proxyClassName, asmFieldName, Type.getDescriptor(Method.class));
    }
Copy the code

Generating methods

private static void addSuperMethodImpl(ClassWriter cw, Class
        superClass, String proxyClassName) {
    Method[] methods = getMethods(superClass);
    for (int i = 0; i < methods.length; i++) {
        Method method = methods[i];
        String asmMethodName = "_asm_" + method.getName() + "_" + i;
        String methodName = method.getName();
        String fieldName = "_METHOD_" + method.getName() + i;
        String asmFieldName = "_METHOD_ASM_"+ methods[i].getName() + i; createSuperMethod(cw, superClass, method, asmMethodName); createProxyMethod(cw, proxyClassName, method, methodName, fieldName, asmFieldName); }}// Generate a proxy method that calls MethodInterceptor
private static void createProxyMethod(ClassWriter cw, String proxyClassName, Method method, String methodName, String fieldName, String asmFieldName) {
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, methodName, Type.getMethodDescriptor(method), null.new String[]{Type.getInternalName(Exception.class)});
    mv.visitCode();
    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassName, "methodInterceptor", Type.getDescriptor(MethodInterceptor.class));
    mv.visitVarInsn(Opcodes.ALOAD, 0);
    mv.visitFieldInsn(Opcodes.GETSTATIC, proxyClassName, fieldName, Type.getDescriptor(Method.class));
    switch (method.getParameterCount()) {
        case 0:
            mv.visitInsn(Opcodes.ICONST_0);
            break;
        case 1:
            mv.visitInsn(Opcodes.ICONST_1);
            break;
        case 2:
            mv.visitInsn(Opcodes.ICONST_2);
            break;
        case 3:
            mv.visitInsn(Opcodes.ICONST_3);
            break;
        default:
            mv.visitVarInsn(Opcodes.BIPUSH, method.getParameterCount());
            break;
    }
    mv.visitTypeInsn(Opcodes.ANEWARRAY, Type.getInternalName(Object.class));
    for (int paramIndex = 0; paramIndex < method.getParameterCount(); paramIndex++) {
        mv.visitInsn(Opcodes.DUP);
        switch (paramIndex) {
            case 0:
                mv.visitInsn(Opcodes.ICONST_0);
                break;
            case 1:
                mv.visitInsn(Opcodes.ICONST_1);
                break;
            case 2:
                mv.visitInsn(Opcodes.ICONST_2);
                break;
            case 3:
                mv.visitInsn(Opcodes.ICONST_3);
                break;
            default:
                mv.visitVarInsn(Opcodes.BIPUSH, paramIndex);
                break;
        }
        mv.visitVarInsn(Opcodes.ALOAD, paramIndex + 1);
        mv.visitInsn(Opcodes.AASTORE);
    }
    mv.visitFieldInsn(Opcodes.GETSTATIC, proxyClassName, asmFieldName, Type.getDescriptor(Method.class));
    mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(MethodInterceptor.class), "intercept"."(Ljava/lang/Object; Ljava/lang/reflect/Method; [Ljava/lang/Object;Ljava/lang/reflect/Method;)Ljava/lang/Object;".true);
    addReturnWithCheckCast(mv, method.getReturnType());
    mv.visitMaxs(2.2);
    mv.visitEnd();
}
// Generate a method that calls the parent class
private static void createSuperMethod(ClassWriter cw, Class
        superClass, Method method, String methodName) {
    MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, methodName, Type.getMethodDescriptor(method), null.new String[]{Type.getInternalName(Throwable.class)});
    mv.visitCode();
    int parameterCount = method.getParameterCount();
    for (int index = 0; index <= parameterCount; index++) {
        mv.visitVarInsn(Opcodes.ALOAD, index);
    }
    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(superClass), method.getName(), Type.getMethodDescriptor(method), false);
    addReturnNoCheckCast(mv, method.getReturnType());
    mv.visitMaxs(2.2);
    mv.visitEnd();
}

// Add method return, need to type
private static void addReturnWithCheckCast(MethodVisitor mv, Class
        returnType) {
    if (returnType.isAssignableFrom(Void.class) || "void".equals(returnType.getName())) {
        mv.visitInsn(Opcodes.RETURN);
        return;
    }
    if (returnType.isAssignableFrom(boolean.class)) {
        mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Boolean.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Boolean.class), "booleanValue"."()Z".false);
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(int.class)) {
        mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Integer.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Integer.class), "intValue"."()I".false);
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(long.class)) {
        mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Long.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Long.class), "longValue"."()J".false);
        mv.visitInsn(Opcodes.LRETURN);
    } else if (returnType.isAssignableFrom(short.class)) {
        mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Short.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Short.class), "shortValue"."()S".false);
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(byte.class)) {
        mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Byte.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Byte.class), "byteValue"."()B".false);
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(char.class)) {
        mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Character.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Character.class), "charValue"."()C".false);
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(float.class)) {
        mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Float.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Float.class), "floatValue"."()F".false);
        mv.visitInsn(Opcodes.FRETURN);
    } else if (returnType.isAssignableFrom(double.class)) {
        mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(Double.class));
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Double.class), "doubleValue"."()D".false);
        mv.visitInsn(Opcodes.DRETURN);
    } else{ mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(returnType)); mv.visitInsn(Opcodes.ARETURN); }}// Add method returns, no type conversion required
private static void addReturnNoCheckCast(MethodVisitor mv, Class
        returnType) {
    if (returnType.isAssignableFrom(Void.class) || "void".equals(returnType.getName())) {
        mv.visitInsn(Opcodes.RETURN);
        return;
    }
    if (returnType.isAssignableFrom(boolean.class)) {
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(int.class)) {
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(long.class)) {
        mv.visitInsn(Opcodes.LRETURN);
    } else if (returnType.isAssignableFrom(short.class)) {
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(byte.class)) {
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(char.class)) {
        mv.visitInsn(Opcodes.IRETURN);
    } else if (returnType.isAssignableFrom(float.class)) {
        mv.visitInsn(Opcodes.FRETURN);
    } else if (returnType.isAssignableFrom(double.class)) {
        mv.visitInsn(Opcodes.DRETURN);
    } else{ mv.visitInsn(Opcodes.ARETURN); }}Copy the code

After generating the bytecode array, you also need a custom class loader to load the class

  • Defines the relationship between Map storage class names and byte arrays
  • Call the Add method to add the relationship before loading the class
  • Call loadClass to get the Class
package simple;

import java.util.HashMap;
import java.util.Map;

public class ASMClassLoader extends ClassLoader {
    private final Map<String, byte[]> classMap = new HashMap<>();

    @Override
    protectedClass<? > findClass(String name)throws ClassNotFoundException {
        if (classMap.containsKey(name)) {
            byte[] bytes = classMap.get(name);
            classMap.remove(name);
            return defineClass(name, bytes, 0, bytes.length);
        }
        return super.findClass(name);
    }

    public void add(String name, byte[] bytes) { classMap.put(name, bytes); }}Copy the code

After generating the class, compare the result with the cglib call:

package simple.test;

import simple.Enhancer;

public class Main {
    public static void main(String[] args) throws Throwable {
        System.out.println("Cglib dynamic proxy ------------");
        net.sf.cglib.proxy.Enhancer cEnhancer = new net.sf.cglib.proxy.Enhancer();
        cEnhancer.setSuperclass(UserService.class);
        cEnhancer.setCallback(new CUserMethodInterceptor());
        UserService cUserService = (UserService) cEnhancer.create();
        System.out.println(cUserService.getClass().getName());
        System.out.println(cUserService.login("admin"."admin"));
        System.out.println(cUserService.login("admin"."admin1"));
        System.out.println("Asm implements dynamic proxy -----------");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperClass(UserService.class);
        enhancer.setMethodInterceptor(new UserMethodInterceptor());
        UserService service = (UserService) enhancer.create();
        System.out.println(service.getClass().getName());
        System.out.println(service.login("admin"."admin"));
        System.out.println(service.login("admin"."admin1")); }}Copy the code