asm是一款编写字节码的框架,熟练使用可以加深对字节码指令的掌握。
Java的动态代理
Java动态代理是基于接口代理的,所以首先我们得定义一个公共接口。
现在代理用户接口,实现登陆逻辑和来打印登录的花费时间
public interface UserService {
boolean login(String username, String password);
}
- 1.
- 2.
- 3.
再来看看Proxy的使用方法,newProxyInstance方法需要传三个参数,第一个类加载器,第二个需要代理的接口数组,第三个参数是调用方法处理器,也是我们写代理逻辑的需要实现的接口。
实现InvocationHandler,判断传入的username 和password是否等于admin,而且打印调用方法耗时。
public class UserServiceInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
long start = System.currentTimeMillis();
System.out.println("invoke:" + proxy.getClass().getSimpleName() + "." + method.getName() + ":" + (System.currentTimeMillis() - start) + "ms");
return "admin".equals(args[0]) && "admin".equals(args[1]);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
生成代理类
import java.lang.reflect.Proxy;
public class App {
public static void main(String[] args) {
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(App.class.getClassLoader(), new Class[]{UserService.class}, new UserServiceInvocationHandler());
System.out.println(userServiceProxy.getClass());
System.out.println(userServiceProxy.login("admin", "admin"));
System.out.println(userServiceProxy.login("admin", "admin1"));
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
调用main方法,打印结果
使用ASM实现
首先我们先看一下生成的代理类最终的样子
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import proxy.ASMProxy;
import proxy.UserService;
public class $Proxy0 extends ASMProxy implements UserService {
public static Method _UserService_0 = Class.forName("proxy.UserService").getMethod("login", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
public $Proxy0(InvocationHandler var1) {
super(var1);
}
public boolean login(String var1, String var2) throws Exception {
return (Boolean)super.h.invoke(this, _UserService_0, new Object[]{var1, var2});
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
三个要点:
- InvocationHandler保存在ASMProxy中。
- 要实现的接口方法Method使用静态字段保存。
- 实现接口方法内实际调用父类的InvocationHandler的invoke方法。
具体看看实现步骤
ASMProxy
package proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.concurrent.atomic.AtomicInteger;
public class ASMProxy {
protected InvocationHandler h;
//代理类名计数器
private static final AtomicInteger PROXY_CNT = new AtomicInteger(0);
private static final String PROXY_CLASS_NAME_PRE = "$Proxy";
public ASMProxy(InvocationHandler var1) {
h = var1;
}
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)throws Exception {
//生成代理类Class
Class<?> proxyClass = generate(interfaces);
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
return constructor.newInstance(h);
}
/**
* 生成代理类Class
*
* @param interfaces
* @return
*/
private static Class<?> generate(Class<?>[] interfaces) throws ClassNotFoundException {
String proxyClassName = PROXY_CLASS_NAME_PRE + PROXY_CNT.getAndIncrement();
byte[] codes = ASMProxyFactory.generateClass(interfaces, proxyClassName);
//使用自定义类加载器加载字节码
ASMClassLoader asmClassLoader = new ASMClassLoader();
asmClassLoader.add(proxyClassName, codes);
return asmClassLoader.loadClass(proxyClassName);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
ASMProxy的主要功能一个是作为代理类需要继承的父类,接着提供一个和Proxy同样的静态方法newProxyInstance。newProxyInstance里面调用ASMProxyFactory生成字节码二进制流,然后调用自定义的类加载器来生成Class。最后反射生成代理类的实例,返回对象。
ASMProxyFactory
接着看看最核心的部分,ASMProxyFactory是怎样生成字节码的,分几个步骤:
- 创建初始化方法
- 声明静态字段
- 创建静态方法
- 实现接口方法
package proxy;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;
public class ASMProxyFactory {
private static final Integer DEFAULT_NUM = 1;
public static byte[] generateClass(Class<?>[] interfaces, String proxyClassName) {
//创建一个ClassWriter对象,自动计算栈帧和局部变量表大小
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
//创建的java版本、访问标志、类名、父类、接口
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, proxyClassName, null, Type.getInternalName(ASMProxy.class), getInterfacesName(interfaces));
//创建<init>
createInit(cw);
//创建static
addStatic(cw, interfaces);
//创建<clinit>
addClinit(cw, interfaces, proxyClassName);
//实现接口方法
addInterfacesImpl(cw, interfaces, proxyClassName);
cw.visitEnd();
return cw.toByteArray();
}
private static String[] getInterfacesName(Class<?>[] interfaces) {
String[] interfacesName = new String[interfaces.length];
return Arrays.stream(interfaces).map(Type::getInternalName).collect(Collectors.toList()).toArray(interfacesName);
}
/**
* 创建init方法
* 调用父类的构造方法
* 0 aload_0
* 1 aload_1
* 2 invokespecial #1 <proxy/ASMProxy.<init> : (Ljava/lang/reflect/InvocationHandler;)V>
* 5 return
*
* @param cw
*/
private static void createInit(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", null, null);
mv.visitCode();
//将this入栈
mv.visitVarInsn(Opcodes.ALOAD, 0);
//将参数入栈
mv.visitVarInsn(Opcodes.ALOAD, 1);
//调用父类初始化方法
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(ASMProxy.class), "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", false);
// 返回
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
/**
* 创建static字段
*
* @param cw
* @param interfaces
*/
private static void addStatic(ClassWriter cw, Class<?>[] interfaces) {
for (Class<?> anInterface : interfaces) {
for (int i = 0; i < anInterface.getMethods().length; i++) {
String methodName = "_" + anInterface.getSimpleName() + "_" + i;
cw.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, methodName, Type.getDescriptor(Method.class), null, null);
}
}
}
private static void addClinit(ClassWriter cw, Class<?>[] interfaces, String proxyClassName) {
//_UserService_0 = Class.forName("proxy.UserService").getMethod("login", String.class, String.class);
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
for (Class<?> anInterface : interfaces) {
for (int i = 0; i < anInterface.getMethods().length; i++) {
Method method = anInterface.getMethods()[i];
String methodName = "_" + anInterface.getSimpleName() + "_" + i;
mv.visitLdcInsn(anInterface.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);
}
}
// invokevirtual #13 <java/lang/Class.getMethod : (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;>
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Class.class), "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
//putstatic #3 <proxy/$Proxy0._UserService_0 : Ljava/lang/reflect/Method;>
mv.visitFieldInsn(Opcodes.PUTSTATIC, proxyClassName, methodName, Type.getDescriptor(Method.class));
}
mv.visitInsn(Opcodes.RETURN);
}
mv.visitMaxs(DEFAULT_NUM, DEFAULT_NUM);
mv.visitEnd();
}
private static void addInterfacesImpl(ClassWriter cw, Class<?>[] interfaces, String proxyClassName) {
for (Class<?> anInterface : interfaces) {
for (int i = 0; i < anInterface.getMethods().length; i++) {
Method method = anInterface.getMethods()[i];
String methodName = "_" + anInterface.getSimpleName() + "_" + i;
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, new String[]{Type.getInternalName(Exception.class)});
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, Type.getInternalName(ASMProxy.class), "h", "Ljava/lang/reflect/InvocationHandler;");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETSTATIC, proxyClassName, methodName, 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));
// * 12 dup
// * 13 iconst_0
// * 14 aload_1
// * 15 aastore
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);
}
// * 20 invokeinterface #5 <java/lang/reflect/InvocationHandler.invoke : (Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;> count 4
// * 25 checkcast #6 <java/lang/Boolean>
// * 28 invokevirtual #7 <java/lang/Boolean.booleanValue : ()Z>
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(InvocationHandler.class), "invoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true);
addReturn(mv, method.getReturnType());
// mv.visitFrame(Opcodes.F_FULL, 0, null, 0, null);
mv.visitMaxs(DEFAULT_NUM, DEFAULT_NUM);
mv.visitEnd();
}
}
}
//添加方法返回
private static void addReturn(MethodVisitor mv, Class<?> returnType) {
if (returnType.isAssignableFrom(Void.class)) {
mv.visitInsn(Opcodes.RETURN);
return;
}
if (returnType.isAssignableFrom(boolean.class)) {
//checkcast #6 <java/lang/Boolean>
// * 28 invokevirtual #7 <java/lang/Boolean.booleanValue : ()Z>
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.JRETURN);
} 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);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
ASMClassLoader
自定义类加载器,提供add方法,添加<类名、字节码>映射关系。覆写findClass方法,当类名能找到对应字节码时,调用defineClass生成Class。
package proxy;
import java.util.HashMap;
import java.util.Map;
public class ASMClassLoader extends ClassLoader {
private final Map<String, byte[]> classMap = new HashMap<>();
@Override
protected Class<?> 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);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
App
package proxy;
import java.lang.reflect.Proxy;
public class App {
public static void main(String[] args) throws Throwable {
System.out.println("Java动态代理===========================");
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(App.class.getClassLoader(), new Class[]{UserService.class}, new UserServiceInvocationHandler());
System.out.println(userServiceProxy.getClass());
System.out.println(userServiceProxy.login("admin", "admin"));
System.out.println(userServiceProxy.login("admin", "admin1"));
System.out.println("ASM动态代理===========================");
UserService userServiceAsm = (UserService) ASMProxy.newProxyInstance(App.class.getClassLoader(), new Class[]{UserService.class}, new UserServiceInvocationHandler());
System.out.println(userServiceAsm.getClass());
System.out.println(userServiceAsm.login("admin", "admin"));
System.out.println(userServiceAsm.login("admin", "admin1"));
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
运行App:打印两种代理方式的结果