java动态代理(类加载、asm、cglib、javassist)

class文件简介及加载

Java编译器编译好Java文件之后,产生.class 文件在磁盘中。这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码。JVM虚拟机读取字节码文件,取出二进制数据,加载到内存中,解析.class 文件内的信息,生成对应的 Class对象:


class字节码文件是根据JVM虚拟机规范中规定的字节码组织规则生成的、具体class文件是怎样组织类信息的,可以参考 此博文:深入理解Java Class文件格式系列。或者是Java虚拟机规范

下面通过一段代码演示手动加载 class文件字节码到系统内,转换成class对象,然后再实例化的过程:

a. 定义自己的类加载器:

package classloadertest;

public class MyClassLoader<T> extends ClassLoader {

    /**
     * 自定义一个类加载器,用于将字节码转换为class对象 
     * @param name   The expected binary name of the class, or null if not known
     * @param bs
     * @param off
     * @param len
     * @return
     */
    @SuppressWarnings("unchecked")
    public Class<T> defineMyClass(String name, byte[] bs, int off, int len) {
        return (Class<T>) super.defineClass(name, bs, off, len);
    }
}


b. 测试:

package classloadertest;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import javax.persistence.UniqueConstraint;
import sun.reflect.CallerSensitive;

/**
 * 程序猿类
 * @author zhangqingli
 *
 */
public class Programmer {

    public void code() {
        System.out.println("I am a Programmer, just coding ...");
    }

    public static void main(String[] args) throws Exception {
        // 获取字节码数组
        String classRoot = Programmer.class.getResource("/").getPath();
        InputStream is = new FileInputStream(classRoot+"/classloadertest/Programmer.class");
        byte[] bs = new byte[is.available()];
        int len = is.read(bs);

        // 将字节码数组转化为内存中的类对象
        Class<Programmer> clazz = new MyClassLoader<Programmer>().defineMyClass(null, bs, 0, len);
        System.out.println("生成的class对象为:" + clazz.getCanonicalName()); 

        // 利用反射生成对象并调用对象的方法
        Object o = clazz.newInstance();
        clazz.getMethod("code", null).invoke(o, null);
    }
}



在运行期的代码中生成二进制字节码

由于JVM通过字节码的二进制信息加载类的,那么,如果我们在运行期系统中,遵循Java编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类,这样,就完成了在代码中,动态创建一个类的能力了。


在运行时期可以按照Java虚拟机规范对class文件的组织规则生成对应的二进制字节码。当前有很多开源框架可以完成这些功能,如ASM,Javassist。

Java字节码生成开源框架介绍–ASM:

ASM 是一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

不过ASM在创建class字节码的过程中,操纵的级别是底层JVM的汇编指令级别,这要求ASM使用者要对class组织结构和JVM汇编指令有一定的了解。

下面通过ASM 生成下面类Programmer的class字节码:

package asmtest;

/**
 * 程序猿类
 * @author zhangqingli
 *
 */
public class Programmer {

    public void code() {
        System.out.println("I am a Programmer, just coding ...");
    }
}


使用ASM框架提供了ClassWriter 接口,通过访问者模式进行动态创建class字节码,看下面的例子:

package asmtest;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import net.sf.cglib.asm.ClassWriter;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;

/**
 * 使用asm动态创建class字节码
 * @author zhangqingli
 *
 */
public class MyGenerator {

    public static void main(String[] args) throws IOException {
        ClassWriter classWriter = new ClassWriter(0);  
        // 通过visit方法确定类的头部信息  
        classWriter.visit(Opcodes.V1_7, // java版本  
                Opcodes.ACC_PUBLIC,     // 类修饰符  
                "Programmer",           // 类的全限定名  
                null, "java/lang/Object", null);  

        //创建构造函数  
        MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);  
        mv.visitCode();  
        mv.visitVarInsn(Opcodes.ALOAD, 0);  
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>","()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.....");  
        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 dir = new File(System.getProperty("user.dir")+"/tmp"); 
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File file = new File(dir, "Programmer.class");
        //
        FileOutputStream fout = new FileOutputStream(file);  
        fout.write(data);  
        fout.close();  
    }
}


上述的代码执行过后,用Java反编译工具(如JD_GUI)可以看到生成的Programmer.class字节码文件。

Java字节码生成开源框架介绍–Javassist:

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态AOP框架。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

下面通过Javassist创建上述的Programmer类:

package javassisttest;

import java.io.File;
import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

/**
 * 通过Javassist创建Programmer.class字节码
 * @author zhangqingli
 *
 */
public class MyGenerater {

    public static void main(String[] args) throws CannotCompileException, IOException {
        ClassPool pool = ClassPool.getDefault();

        // 创建Programmer类
        CtClass cc = pool.makeClass("javassisttest.Programmer");
        // 定义code方法
        CtMethod codeMethod = CtNewMethod.make("public void code(){}", cc);
        // 插入方法代码
        codeMethod.insertBefore("System.out.print(\"哈哈哈before\");");
        cc.addMethod(codeMethod);

        //保存生成的字节码
        File dir = new File(System.getProperty("user.dir")+"/tmp"); 
        if (!dir.exists()) {
            dir.mkdirs();
        }
        cc.writeFile(dir.getCanonicalPath());
    }
}


java代理(需要实现特定接口)

package cglibtest;

/**
 * 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口
 * 
 */
public interface IService01 {
    void add();
    void update();
}




package cglibtest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * jdk动态代理需要目标类基于统一的接口
 *
 */
public class JdkAopTest01 {
    public static void main(String[] args) {
        //生成代理类
        IService01 iServiceProxy = (IService01) MyInvocationHandler01.generateProxyInstance(new Service01());

        //使用代理类
        iServiceProxy.add();
        iServiceProxy.update();
    }
}


//被代理类,即目标类target
class Service01 implements IService01 {

    @Override
    public void add() {
        System.out.println("Service01 add >>>>>>>>>>>>>>");
        Integer.parseInt("aaa");
    }

    @Override
    public void update() {
        System.out.println("Service01 update >>>>>>>>>>>>>>");
    }
}

//切面处理类InvocationHandler
class MyInvocationHandler01 implements InvocationHandler {
    private Object target;
    public MyInvocationHandler01(Object target) {
        this.target = target;
    }

    // 生成代理类
    public static Object generateProxyInstance(Object target) {
        MyInvocationHandler01 myInvocationHandler01 = new MyInvocationHandler01(target);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(),
                myInvocationHandler01);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //程序执行前加入逻辑,MethodBeforeAdviceInterceptor
        System.out.println("before------------------");

        //程序执行
        Object result = null;
        try {
            result = method.invoke(target, args);
        } catch (Exception e) {
            System.out.println("=========异常=========");
        } finally {
            System.out.println("=======执行finally========");
        }

        //程序执行后加入逻辑,MethodAfterAdviceInterceptor  
        System.out.println("after------------------------------");
        return result;
    }
}



cglib代理(不需要实现特定的接口,它是通过生成目标子类实现代理的)

package cglibtest;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
 * cglib动态代理不需要目标类基于统一的接口
 * @author zhangqingli
 *
 */
public class CglibProxyTest02 {
    public static void main(String[] args) {
        //生成代理类
        Service02 serviceProxy = (Service02) MyInterceptor.generateProxyInstance(Service02.class);

        //使用代理类
        System.out.println(serviceProxy.add(100));
        serviceProxy.update();
    }
}



//被代理类,即目标对象target
class Service02 {

    public Integer add(Integer id) {
        System.out.println("Service02 add >>>>>>>>>>>>>>>>>");
        Integer.parseInt("aaa");
        return id;
    }

    public void update() {
        System.out.println("Service02 update >>>>>>>>>>>>>>>");
    }
}

//切面处理类,MethodInterceptor
class MyInterceptor implements MethodInterceptor {

    // 生成代理对象
    public static Object generateProxyInstance(Class<?> targetType) {
        MyInterceptor myInterceptor = new MyInterceptor();
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetType);
        enhancer.setCallback(myInterceptor);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        //程序执行前加入逻辑,MethodBeforeAdviceInterceptor
        System.out.println("before--------------------------");

        //程序执行
        Object result = null;
        try {
            result = methodProxy.invokeSuper(obj, args); //使用method和methodProxy都能调用invoke方法,并且执行效果一样
        } catch (Exception e) {
            System.out.println("===========异常===========");
            throw e;
        } finally {
            System.out.println("=========执行finally==========");
        }


        //程序执行后加入逻辑,MethodAfterAdviceInterceptor
        System.out.println("after--------------------------");
        return result;
    }
}


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值