使用ASM动态创建接口实现类

使用ASM动态生成一个接口的实现类,接口如下:

java 复制代码
public interface ISayHello {
	public void MethodA();
	public void MethodB();
	public void Abs();
}

具体实现如下:

java 复制代码
public class InterfaceHandler extends ClassLoader implements Opcodes {
	public static Object MakeClass(Class<?> clazz) throws Exception {
		String name = clazz.getSimpleName();
		String className = name + "$imp";
 
		String Iter = clazz.getName().replaceAll("\\.", "/");
 
		ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, className, null,
				"java/lang/Object", new String[] { Iter });
 
		// 空构造
		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null,
				null);
		mv.visitVarInsn(ALOAD, 0);
		mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
		mv.visitInsn(RETURN);
		mv.visitMaxs(1, 1);
		mv.visitEnd();
 
		// 实现接口中所有方法
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			MakeMethod(cw, method.getName(), className);
		}
		cw.visitEnd();
		
		//写入文件
		byte[] code = cw.toByteArray();
		FileOutputStream fos = new FileOutputStream("d:/com/" + className + ".class");
		fos.write(code);
		fos.close();
 
		//从文件加载类
		InterfaceHandler loader = new InterfaceHandler();
		Class<?> exampleClass = loader.defineClass(className, code, 0,
				code.length);
 
		Object obj = exampleClass.getConstructor().newInstance();
		return obj;
	}
 
	private static void MakeMethod(ClassWriter cw, String MethodName,
			String className) {
		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, MethodName, "()V", null, null);
		//mv.visitCode();
		//Label l0 = new Label();
		//mv.visitLabel(l0);
		//mv.visitLineNumber(8, l0);
		mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
				"Ljava/io/PrintStream;");
		mv.visitLdcInsn("调用方法 [" + MethodName + "]");
		mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
				"(Ljava/lang/String;)V");
		//Label l1 = new Label();
		//mv.visitLabel(l1);
		//mv.visitLineNumber(9, l1);
		mv.visitInsn(RETURN);
		//Label l2 = new Label();
		//mv.visitLabel(l2);
		//mv.visitLocalVariable("this", "L" + className + ";", null, l0, l2, 0);
		mv.visitMaxs(2, 1);
		mv.visitEnd();
	}
	public static void main(final String args[]) throws Exception {
		ISayHello iSayHello = (ISayHello) MakeClass(ISayHello.class);
		iSayHello.MethodA();
		iSayHello.MethodB();
		iSayHello.Abs();
	}
}

注意,使用ASM访问属性和方法的时候,会返回一个Visitor对象,如属性为FieldVisitor,方法为MethodVisitor。

使用反编译工具查看生成的字节码文件内容如下:

java 复制代码
public class ISayHello$imp
  implements ISayHello
{
  public void MethodA()
  {
    System.out.println("调用方法 [MethodA]");
  }
 
  public void MethodB()
  {
    System.out.println("调用方法 [MethodB]");
  }
 
  public void Abs()
  {
    System.out.println("调用方法 [Abs]");
  }
}

使用ASM生成接口实现类

在java的许多框架里面,都能找到ASM的身影,比如AOP编程就可以利visitMethod对指定方法就行拦截,做前置后置增强,还有比如常用的插件Lombok就是利用ASM添加的setter getter方法。MyBatis的Mapper接口实现是通过动态代理实现,现在可以使用ASM动态创建实现了字节码来实现。

1、定义接口

定义StudentMapper接口

java 复制代码
package org.example.cn.mapper;
import java.util.List;
 
public interface StudentMapper {
    List<String>  findStudentList();
}
2、生成实现类

使用ASM生成实现类StudentMapperImpl

java 复制代码
        ClassWriter classWriter = new ClassWriter(0);
        /// 参数列表第3个参数表示继承的父类,第4个参数表示实现的接口
        classWriter.visit(61, ACC_PUBLIC | ACC_SUPER, "org/example/cn/mapper/StudentMapperImpl", null, "java/lang/Object", new String[]{"org/example/cn/mapper/StudentMapper"});
        classWriter.visitSource("StudentMapperImpl.java", null);
 
        /// 定义构造器
        MethodVisitor  methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        methodVisitor.visitCode();
        Label initLabel0 = new Label();
        methodVisitor.visitLabel(initLabel0);
        methodVisitor.visitVarInsn(ALOAD, 0);
        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        methodVisitor.visitInsn(RETURN);
        Label initLabel1 = new Label();
        methodVisitor.visitLabel(initLabel1);
        /// 局部变量表,槽位的0处放的是this变量
        methodVisitor.visitLocalVariable("this", "Lorg/example/cn/mapper/StudentMapperImpl;", null, initLabel0, initLabel1, 0);
        methodVisitor.visitMaxs(1, 1);
        methodVisitor.visitEnd();
 
        /// 重写findStudentList方法
        methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "findStudentList", "()Ljava/util/List;", "()Ljava/util/List<Ljava/lang/String;>;", null);
        methodVisitor.visitCode();
        Label label0 = new Label();
        methodVisitor.visitLabel(label0);
        ///  new ArrayList()
        methodVisitor.visitTypeInsn(NEW, "java/util/ArrayList");
        methodVisitor.visitInsn(DUP);
        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V");
        methodVisitor.visitVarInsn(ASTORE, 1);
        Label label1 = new Label();
        methodVisitor.visitLabel(label1);
        /// 压栈
        methodVisitor.visitVarInsn(ALOAD, 1);
        /// 赋值
        methodVisitor.visitLdcInsn("\u9648\u7476");
        ///调用add方法
        methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z");
        /// 出栈
        methodVisitor.visitInsn(POP);
        Label label2 = new Label();
        methodVisitor.visitLabel(label2);
        methodVisitor.visitVarInsn(ALOAD, 1);
        methodVisitor.visitLdcInsn("\u674e\u73b0");
        methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z");
        methodVisitor.visitInsn(POP);
        Label label3 = new Label();
        methodVisitor.visitLabel(label3);
        methodVisitor.visitLineNumber(11, label3);
        methodVisitor.visitVarInsn(ALOAD, 1);
        methodVisitor.visitLdcInsn("\u91d1\u6668");
        methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z");
        methodVisitor.visitInsn(POP);
        Label label4 = new Label();
        methodVisitor.visitLabel(label4);
        methodVisitor.visitLineNumber(12, label4);
        methodVisitor.visitVarInsn(ALOAD, 1);
        methodVisitor.visitInsn(ARETURN);
        Label label5 = new Label();
        methodVisitor.visitLabel(label5);
        methodVisitor.visitLocalVariable("this", "Lorg/example/cn/mapper/StudentMapperImpl;", null, label0, label5, 0);
        methodVisitor.visitLocalVariable("list", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", label1, label5, 1);
        methodVisitor.visitMaxs(2, 2);
        methodVisitor.visitEnd();
 
        classWriter.visitEnd();
        byte[] bytes = classWriter.toByteArray();

字节码java源码

java 复制代码
public class StudentMapperImpl implements StudentMapper {
 
    public List<String> findStudentList() {
        List<String> list = new ArrayList();
        list.add("陈瑶");
        list.add("李现");
        list.add("金晨");
        return list;
    }
}
3、加载实现类

自定义类加载器加载StudentMapperImpl字节码

java 复制代码
public class MyClassLoader  extends  ClassLoader{
 
    /// 类名全路径
    private final String   className;
 
    /// 字节码
    private final byte[]  bytes;
 
    public MyClassLoader( String  className,byte[]  bytes){
        this.className = className;
        this.bytes = bytes;
    }
 
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return  defineClass(className,bytes,0,bytes.length);
    }
}
加载类
java 复制代码
    String className = "org.example.cn.mapper.StudentMapperImpl";
    MyClassLoader myClassLoader = new MyClassLoader(className, bytes);
    Class<?>  clz =  myClassLoader.loadClass(className);
4、调用实现类
◆ 方法1

利用构造器直接反射创建实例

java 复制代码
   /// Class<?> aClass = Class.forName("org.example.cn.mapper.StudentMapperImpl");
   StudentMapper studentMapper = (StudentMapper) clz.getConstructor().newInstance();
   List<String> studentList = studentMapper.findStudentList();
   System.out.println("studentList---"+studentList);
方法2

动态代理实例化接口

java 复制代码
    StudentMapper studentMapper = (StudentMapper) Proxy.newProxyInstance(myClassLoader, new Class[]{StudentMapper.class}, (proxy, method, args1) -> method.invoke(clz.getConstructor().newInstance(), args1));
    List<String> studentList = studentMapper.findStudentList();
    System.out.println("studentList---"+studentList);

运行结果

​​​​​​​

相关推荐
yaoxtao7 分钟前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区1 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT2 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy2 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss4 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续4 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0444 小时前
ReAct模式解读
java·ai
轮到我狗叫了5 小时前
牛客.小红的子串牛客.kotori和抽卡牛客.循环汉诺塔牛客.ruby和薯条
java·开发语言·算法
Volunteer Technology6 小时前
三高项目-缓存设计
java·spring·缓存·高并发·高可用·高数据量