栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java都有哪些动态代理机制?

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java都有哪些动态代理机制?

1:java都有哪些动态代理机制

据我所以所指,目前有JDK动态代理,javassist,cglib,asm,实现方式不尽相同,但是原理相同。

2:动态代理实现原理分析

正常的开发流程是,编译器编译java源代码生成.class的字节码文件,但是需要注意,.class其实也是一种符合某种规范的文件,只不过并不是人类可读的文件,而是一种二进制文件,那么这种文件谁能够解析呢,JVM,JVM能够加载这些文件,经过各种各系工作之后,生成Class对象,其中执行加载工作的是类加载器抽象类java.lang.ClassLoader,详细的过程如下图:

那么动态代理在哪里发挥作用呢?答案是在运行期动态的生成符合JVM规范的class文件,执行重新加载的工作,比如动态创建某个已加载的类的子类的class字节码文件,然后在执行加载的工作,此时这个过程如下图:


这就是动态打理的原理,当拥有了这样的动态编写代码的能力之后,任何代码我们都能够动态生成了,功能就会变得相当强大了。
下面我们来看一个使用类加载器加载的例子。

2.1:定义待加载的类
  • 源代码
package yudaosourcecode.huohuo;

public class Programmer {
 
	public void code() {
		System.out.println("I'm a Programmer,Just Coding.....");
	}
}
2.2:定义类加载器
  • 源码
package yudaosourcecode.huohuo;

public class MyClassLoader extends ClassLoader {
 
	public Class defineMyClass( byte[] b, int off, int len) {
		return super.defineClass(b, off, len);
	}
	
}
2.3:定义测试类

该类使用2.2:定义类加载器中定义的类加载器加载2.1:定义待加载的类的.class文件到JVM中生成Class对象,然后使用Class对象创建实例对象。

package yudaosourcecode.huohuo;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;

public class MyTest {

    public static void main(String[] args) throws Exception {
        //读取本地的class文件内的字节码,转换成字节码数组
        File file = new File(".");
        InputStream input =
                new FileInputStream("/Users/xb/Desktop/D/dongsir-dev/java-life-current/java-life/target/classes/yudaosourcecode/huohuo/Programmer.class");
        byte[] result = new byte[1024];

        int count = input.read(result);
        // 使用自定义的类加载器将 byte字节码数组转换为对应的class对象
        MyClassLoader loader = new MyClassLoader();
        Class clazz = loader.defineMyClass(result, 0, count);
        //测试加载是否成功,打印class 对象的名称
        System.out.println(clazz.getCanonicalName());
        //实例化一个Programmer对象
        Object o = clazz.newInstance();
        try {
            //调用Programmer的code方法
            clazz.getMethod("code", null).invoke(o, null);
        } catch (IllegalArgumentException | InvocationTargetException
                | NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
    }
}

运行:

yudaosourcecode.huohuo.Programmer
I'm a Programmer,Just Coding.....

可以看到执行成功了,动态代理的执行过程也大概如此,只不过,加载的并不是磁盘中现成的class文件,而是在代码中动态生成字节码文件,然后使用类加载器执行动态加载。而当前Java中有些框架就提供了了这样的能力,如asm,javasist等。

3:asm

asm对于使用者的要求较高,需要熟悉字节码文件的格式,了解字节码相关的指令,如如何设置版本号,描述方法签名,类签名等,都必须和字节码文件一一对应。如下是是用asm来实现如下的程序对应的class字节码文件:

package yudaosourcecode.huohuo;
 
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class MyGenerator {
 
	public static void main(String[] args) throws IOException {
 
		System.out.println();
		ClassWriter classWriter = new ClassWriter(0);
		// 通过visit方法确定类的头部信息
		classWriter.visit(Opcodes.V1_8,// java版本
				Opcodes.ACC_PUBLIC,// 类修饰符
				"Programmer", // 类的全限定名
				null, "java/lang/Object", null);
		
		//创建构造函数
		MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null);
		mv.visitCode();
		mv.visitVarInsn(Opcodes.ALOAD, 0);
		mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "","()V");
		mv.visitInsn(Opcodes.RETURN);
		mv.visitMaxs(1, 1);
		mv.visitEnd();
		
		// 定义code方法
		MethodVisitor methodVisitor = classWriter.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 Programmer,Just Coding.....by asm!!!");
		methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
				"(Ljava/lang/String;)V");
		methodVisitor.visitInsn(Opcodes.RETURN);
		methodVisitor.visitMaxs(2, 2);
		methodVisitor.visitEnd();
		classWriter.visitEnd(); 
		// 使classWriter类已经完成
		// 将classWriter转换成字节数组写到文件里面去
		byte[] data = classWriter.toByteArray();
		File file = new File("/Users/xb/Desktop/temp/mycls/Programmer.class");
		FileOutputStream fout = new FileOutputStream(file);
		fout.write(data);
		fout.close();
	}
}

运行生成文件如下:

使用jd-gui查看生成的字节码内容如下:

然后使用2.3:定义测试类中的类进行测试(注意修改字节码文件路径),输出结果如下:

Programmer
I'm a Programmer,Just Coding.....by asm!!!
4:javassist

如果说asm是面向字节码来动态生成字节码文件的话,那么javassit就是面向java源代码来生成字节码文件的,因此javassit的难度相对于和asm要小的多,复杂度低得多,编程效率高得多,下面使用javassit来动态生成一个字节码文件:

package yudaosourcecode.huohuo;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
 
public class MyGeneratorByJavassit {
 
	public static void main(String[] args) throws Exception {
		ClassPool pool = ClassPool.getDefault();
        //创建Programmer类		
		CtClass cc= pool.makeClass("com.samples.ProgrammerJavassit");
		//定义code方法
		CtMethod method = CtNewMethod.make("public void code(){}", cc);
		//插入方法代码
		method.insertBefore("System.out.println("I'm a Programmer,Just Coding.....by javassit!!!");");
		cc.addMethod(method);
		//保存生成的字节码
		cc.writeFile("/Users/xb/Desktop/temp/mycls");
	}
}

生成字节码如下:


然后使用2.3:定义测试类中的类进行测试(注意修改字节码文件路径),输出结果如下:

com.samples.ProgrammerJavassit
I'm a Programmer,Just Coding.....by javassit!!!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/294410.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号