jdk代理 cglib代理 asm javassist

发布时间 2023-06-27 18:24:11作者: 花开如梦

jdk代理实现代码:

public class JdkProxyC implements InvocationHandler {
    private Object target;

    public JdkProxyC(Object target) {
        this.target = target;
    }

    public Object getProxy() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        /**
         * ClassLoader loader,
         * Class<?>[] interfaces,
         * InvocationHandler h
         */
        ClassLoader loader = this.target.getClass().getClassLoader();
        Class<?>[] interfaces = this.target.getClass().getInterfaces();
        InvocationHandler handler = this; //实现 InvocationHandler 接口的类

        Object instance = Proxy.newProxyInstance(loader, interfaces, handler);
        return instance;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理前----->");
        Object obj = method.invoke(this.target, args);
        System.out.println("代理后----->");
        return obj;
    }

    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        //设置开启代理类.class文件的输出
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

        UserServer o = (UserServer) new JdkProxyC(new UserServerImpl()).getProxy();//必须转成接口
        o.proxyMethod();
    }
}
View Code
ProxyGenerator.generateProxyClass()接口输出jdk代理类的字节码,里面的 saveGeneratedFiles 开关:
private static final boolean saveGeneratedFiles =
        java.security.AccessController.doPrivileged(
                new GetBooleanAction(
                        "jdk.proxy.ProxyGenerator.saveGeneratedFiles"));

是"jdk.proxy.ProxyGenerator.saveGeneratedFiles“值的由来,默认是false, 在main函数中设置 System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); 开启这个开关,就会输出代理类的 .class文件,方便查看代理过程

 

cglib代理实现代码:

public class CglibProxy implements MethodInterceptor, CallbackFilter {

    private Object target;

    public CglibProxy(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());//设置类、接口
        enhancer.setCallback(this);// 实现 MethodInterceptor 接口的类

        //设置Callback[] callbacks 数组
        //enhancer.setCallbacks(callbacks);
        //实现 CallbackFilter接口  方法返回CallbackFilter数组下标
        //enhancer.setCallbackFilter(this);

        return enhancer.create();//返回 创建代理类
    }

    /**
     * @param o           增强对象
     * @param method      调用方法
     * @param objects     方法参数
     * @param methodProxy 调用父类方法的代理
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理前----->");
        //一下三种方式均可调用方法,通过代理类源码,可以分析其不同
//        method.invoke(this.target, objects);
//        methodProxy.invoke(this.target, objects);//target速度更快
//        methodProxy.invokeSuper(o, objects);//o:代理对象
        System.out.println("代理后----->");
        return null;
    }

    @Override
    public int accept(Method method) {
        return 0;//默认返回0
    }

    public static void main(String[] args) {
        UserServerImpl proxy = (UserServerImpl) new CglibProxy(new UserServerImpl()).getProxy();
        proxy.proxyMethod();
    }
}
View Code

cglib代理类的对应 .class文件的输出需要如下配置,没有“VM选项的”,需要点击“修改选项”选中“添加VM选项”,然后增加  -Dcglib.debugLocation=F:\WorkSpaceJava\spring_ioc_configration\jdk\cglib 等号后边是你想要输出的代理类字节文件的路径

cglib代理类的生成是通过asm进行字节码生成的。 字节码操作的另一项技术是 javassist

asm 需要依赖的包:

<!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>9.5</version>
</dependency>

asm 通过字节码生成Person字节码类示例:

public class TestAsm {
    public static void main(String[] args) throws IOException {
        ClassWriter classWriter = new ClassWriter(0);

        // 通过visit方法确定类的头部信息
        classWriter.visit(Opcodes.V1_8,// java版本
                Opcodes.ACC_PUBLIC,// 类修饰符
                "Person", // 类的全限定名
                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",false);
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        // 定义test方法
        MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "test", "()V", null, null);
        methodVisitor.visitCode();
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        methodVisitor.visitLdcInsn("hello world!");
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V",false);
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 2);
        methodVisitor.visitEnd();
        classWriter.visitEnd();
        // 使classWriter类已经完成
        // 将classWriter转换成字节数组写到文件里面去
        byte[] data = classWriter.toByteArray();

        File file = new File("F:\\WorkSpaceJava\\spring_ioc_configration\\jdk\\asm\\Person.class");
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(data);
        fout.close();

    }
}
View Code

生成Person字节码类:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

public class Person {
    public Person() {
    }

    public void test() {
        System.out.println("hello world!");
    }
}

 

 javassist需要依赖的包:

<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.29.2-GA</version>
</dependency>

 javassist通过字节码生成Person字节码类示例:

public class TestJavassist {
    public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        ClassPool pool = ClassPool.getDefault();

        // 1. 创建一个空类
        CtClass cc = pool.makeClass("com.test.Person");

        // 2. 新增一个字段 private String name;
        // 字段名为name
        CtField param = new CtField(pool.get("java.lang.String"), "name", cc);
        // 访问级别是 private
        param.setModifiers(Modifier.PRIVATE);
        // 初始值是 "beijing"
        cc.addField(param, CtField.Initializer.constant("beijing"));

        // 3. 生成 getter、setter 方法
        cc.addMethod(CtNewMethod.setter("setName", param));
        cc.addMethod(CtNewMethod.getter("getName", param));

        // 4. 添加无参的构造函数
        CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
        cons.setBody("{name = \"beijing\";}");
        cc.addConstructor(cons);

        // 5. 添加有参的构造函数
        cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
        // $0=this / $1,$2,$3... 代表方法参数
        cons.setBody("{$0.name = $1;}");
        cc.addConstructor(cons);

        // 6. 创建一个名为printName方法,无参数,无返回值,输出name值
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{}, cc);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{System.out.println(name);}");
        cc.addMethod(ctMethod);

        //这里会将这个创建的类对象编译为.class文件
        cc.writeFile("F:\\WorkSpaceJava\\spring_ioc_configration\\jdk\\javassist");



        // 这里不写入文件,直接实例化
//        Object person = cc.toClass().newInstance();
//        // 设置值
//        Method setName = person.getClass().getMethod("setName", String.class);
//        setName.invoke(person, "dadududu");
//        // 输出值
//        Method execute = person.getClass().getMethod("printName");
//        execute.invoke(person);

    }

}
View Code

 

生成Person字节码类:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.test;

public class Person {
    private String name = "beijing";

    public void setName(String var1) {
        this.name = var1;
    }

    public String getName() {
        return this.name;
    }

    public Person() {
        this.name = "beijing";
    }

    public Person(String var1) {
        this.name = var1;
    }

    public void printName() {
        System.out.println(this.name);
    }
}