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