Ⅰ java 几种动态代理实现及其性能比较
1. 动态代理是指在运行时,动态生成代理类。代理类的字节码将在运行时生成并载入当前的ClassLoader.
生成动态代理类的方法很多,如JDK自带的动态代理、CGLIB、Javassist或者ASM库。
JDK动态代理使用简单,它内置在JDK中,因此不需要引入第三方Jar包,但相对功能比较弱。CGLIB和Javassist都是高级的字节码生成库,总体性能比JDK自带的动态代理好,而且功能十分强大。ASM是低级的字节码生成工具,使用ASM已经近乎在于使用Javabytecode编程,对开发人员要求较高,也是性能最好的一种动态代理生辰工具。但ASM的使用是在过于繁琐,而且性能也没有数量级的提升,与CGLIB等高级字节码生成工具相比,ASM程序的可维护性也较差。
JDK实现
1、步骤
1)通过实现InvocationHandler接口创建自己的调用处理器
2)通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类
3)通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
4)通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入
2、创建代理
//InvocationHandlerImpl 实现了InvocationHandler接口,并能实现方法调用从代理类到委托类的分派转发
//其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocaitonHandlerImpl(..);
//通过Proxy为包括Interface接口在内的一组接口动态创建代理类的对象
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{Interface.class,...});
//通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
//通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[]{handler});
//Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。
//InvocationHandlerImpl实现了InvocaitonHandler接口,并能实现方法调用从代理类到委托类的分派转发
InvocaitonHandler handler = new InvocationHandlerImpl(..);
//通过Proxy直接创建动态代理类实例
nterface proxy = (Interface)Proxy.newProxyInstance(classLoader,new Class[]{Interface.class},handler);
3、代码
/**
* 接口
*
*/
public interface IDBQuery {
String request();
}
/**
* 真实的实现类,具体的目标对象
*
*/
public class DBQuery implements IDBQuery {
public DBQuery(){
try {
Thread.sleep(1000); //可能包含数据库连接等耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String request() {
return "request string";
}
}
/**
* JDK动态代理的实现类
*
*/
public class JdkDbQueryHandler implements InvocationHandler{
IDBQuery real = null; //主题接口
/**
* 生成Handler
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(real == null)
real = new DBQuery(); //如果是第一次调用,则生成真实对象
return real.request(); //使用真实主题完成实际的操作
}
/**
* 利用Handler生成动态代理对象
*/
public static IDBQuery createJdkProxy(){
//根据指定的类加载器和接口以及截获器,返回代理类的一个实例对象
//ClassLoader loader :指定被代理对象的类加载器
//Class[] Interfaces :指定被代理对象所以事项的接口
//InvocationHandler h :指定需要调用的InvocationHandler对象
IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new JdkDbQueryHandler());
return jdkProxy;
}
}
Ⅱ java 可以动态创建 属性字段么
可以的,你用 javassist, cglib 或者更为底层的工具 ASM 都是可以。
ASM 的话相对复杂一些,参考代码:
下面这个是用 ASM 工具为 Student 类添加一个 public String 类型的 address 属性:
1,需要添加属性的原始类:Student.java
Java code?
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2,添加属性的适配器:AddFieldAdapter.java
Java code?
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
public class AddFieldAdapter extends ClassAdapter {
private int accessModifier;
private String name;
private String desc;
private boolean isFieldPresent;
public AddFieldAdapter(ClassVisitor cv, int accessModifier, String name, String desc) {
super(cv);
this.accessModifier = accessModifier;
this.name = name;
this.desc = desc;
}
@Override
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
if (name.equals(this.name)) {
isFieldPresent = true;
}
return cv.visitField(access, name, desc, signature, value);
}
@Override
public void visitEnd() {
if (!isFieldPresent) {
FieldVisitor fv = cv.visitField(accessModifier, name, desc, null, null);
if (fv != null) {
fv.visitEnd();
}
}
cv.visitEnd();
}
}
3,添加属性的工具 AddField.java
Java code?
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
public class AddField {
private Class clazz = null;
private ClassReader cr = null;
private ClassWriter cw = null;
private ClassAdapter ca = null;
private File classFile = null;
private final static String CLASS_FILE_SUFFIX = ".class";
public AddField(Class clazz) {
this.clazz = clazz;
}
/**
* 添加一个 public 的类成员
* @param fieldName 类成员名
* @param fieldDesc 类成员类型描述
*/
public void addPublicField(String fieldName, String fieldDesc) {
if(cr == null) {
try {
cr = new ClassReader(clazz.getCanonicalName());
} catch (IOException e) {
e.printStackTrace();
}
cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
}
if(ca == null) {
ca = new AddFieldAdapter(cw, Opcodes.ACC_PUBLIC, fieldName, fieldDesc);
} else {
ca = new AddFieldAdapter(ca, Opcodes.ACC_PUBLIC, fieldName, fieldDesc);
}
}
/**
* 将字节码写入类的 .class 文件
*
*/
public void writeByteCode() {
cr.accept(ca, ClassReader.SKIP_DEBUG);
byte[] bys = cw.toByteArray();
OutputStream os = null;
try {
os = new FileOutputStream(getFile());
os.write(bys);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获得类文件的 File 对象
* @return
*/
private File getFile() {
if(classFile == null) {
StringBuffer sb = new StringBuffer();
sb.append(clazz.getResource("/"))
.append(clazz.getCanonicalName().replace(".", File.separator))
.append(CLASS_FILE_SUFFIX);
classFile = new File(sb.substring(6));
}
return classFile;
}
}
Ⅲ java 的ASM字节码编辑如果替换方法中的类名
等同腔祥于java代码:
System.currentTimeMillis();
替清圆雀换成time.zz()(要答早求time是类名,不是对象,zz是静态方法)
很容易就出错。
Ⅳ Java: asm.jar中的ClassReader方法执行时找不到类---Class not found
【A:JavaProject中的运行结果】你这副图:你看它导入的是哪一个包里面的ClassReader,先声明,这个包不是在asm-5.0.3.jar;我刚看了这个jar,包名是这个org.objectweb.asm。
在java项目中的那个ClassReader你这样用newClassReader("com.test")有用,但是到web里面你用的就是asm-5.0.3.jar中的ClassReader,这时候你newClassReader("com.test")就有问题了!
这是asm里面的构造函数:
publicClassReader(StringparamString)
throwsIOException
{
this(a(ClassLoader.getSystemResourceAsStream(paramString.replace('.','/')+".class"),true));
}
你看它做的事!对于com.test,它会把“.”替换成“/”,然后+“.class”,这时候你哪里有这个类啊!
Ⅳ 史上最通俗易懂的ASM教程
ASM是一种针对Java字节码的强大框架,具备全面的操作和分析能力。它可以用于对已有class文件进行修改或生成全新的class文件。其优点在于能提供一些常用的字节码转换和分析算法,便于开发人员构建定制的复杂转化和代码分析工具,其性能优化设计使其尤其适合动态系统中使用。
使用javac命令编译Java文件为class文件,JVM在执行程序时会使用这些class文件。ASM允许我们深入字节码层级进行操作,更近一步地接触和理解JVM的运行机制。通过ASM,我们能够对类字段、方法定义及局部变量等元素进行直接修改,实现更精细、个性化的类文件定制。
了解ASM之前,我们首先需要掌握class字节码的结构。Java编译器产生的class文件经过多次优化和精简,字段、方法的名称和参数信息都已嵌入常量池中,实现了减小文件体积的目的。方法定义则被转换为JVM指令执行。JVM基于栈的设计模式,通过栈帧来管理方法执行的上下文,每个方法调用创建新的栈帧并执行,之后回退并丢弃旧帧。
局部变量表用于存储方法参数和局部变量,其索引能够快速定位到具体变量。每个变量在局部变量表中占用一个位置,通常变量名、类型、索引号为本地变量表的三个关键元素。操作数栈作为计算支持,用来组织和管理操作数和结果,支持方法执行中的运算和指令执行,通过入栈与出栈操作来控制变量值的存储和使用。
JVM的指令集通过操作数栈和局部变量表共同驱动程序执行,每个指令负责对栈顶元素进行操作,并更新栈或局部变量表。例如在执行`a = b + c`时,首先从操作数栈中获取b和c的值,将结果存储回局部变量表,完成赋值操作。
通过ASM进行字节码编辑,能够更精细地修改代码行为,实现代码重构、功能扩展或代码优化等目的。ASM API采用访问者模式设计,提供如ClassVisitor、MethodVisitor、FieldVisitor等接口,支持在类、方法、字段等不同层次对字节码进行访问和操作。
虽然ASM的操作较为复杂,但借助相关插件如ASM ByteCode Outline,可以大大简化生成ASM API代码的过程,提高开发效率。结合深入学习资源和实践经验,开发者能够充分发挥ASM的强大能力,实现更多可能的代码定制与优化。通过理解和应用ASM,可以更深入掌握字节码层面的操作,提升Java程序性能和灵活性。
Ⅵ java asm label获取调用方法
通过反射调用。
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
ASM 是一个 Java 字节码操控框架,它能被用来动态生成类或者增强既有类的功能,ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。
Ⅶ 史上最通俗易懂的ASM教程
ASM教程,带你探索Java世界的底层秘密。正如王尔德所说,“我们都生活在代码的阴沟里,但ASM让我们能仰望更高级的编程艺术。”这是一种通用的Java字节码操作框架,旨在修改和动态生成class文件,以实现高性能的代码操作。
在深入学习之前,你需要理解class字节码和JVM的基础。Java源文件编译成class文件后,字段、方法名等信息存储在常量池,以减小文件大小。方法定义则转换为JVM指令,这涉及对基于栈的JVM设计模式的理解,其中局部变量表用于存储方法参数和局部变量,操作数栈则负责数据的处理和交换。
举例来说,当你看到"a = b + c"的字节码执行过程,就能理解操作数栈和局部变量表的动态变化。ASM提供ClassVisitor, MethodVisitor, FieldVisitor等API接口,让你能直接操作字节码,实现定制化的需求。虽然使用起来可能有些复杂,但ASM ByteCode Outline这样的插件能极大简化过程。
参考资源如Lyon的《Java虚拟机》、字节码增强技术、kancloud.cn的教程、袁辉辉的技术博客等,深入研究JVM栈帧、操作数栈、局部变量表以及ASM的具体应用。通过学习,你将能掌握ASM这一强大的工具,为你的Java编程旅程增添新维度。