CGLib动态代理和JDK动态代理Demo、ASM技术尝鲜

本文主要介绍CGLib和JDK动态代理的使用,不对源码进行深入分析。代码可直接复制使用。

类型

机制

回调方式

适用场景

效率

JDK动态代理

委托机制。代理类和目标类都实现了同样的接口。InvocationHandler持有目标类。代理类委托InvocationHandler去调用目标类原始方法

反射

目标类实现接口

反射调用稍慢。

CGLIB动态代理

继承机制。代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑(底层使用到ASM技术,操作字节码生成代理类)

通过FastClass方法索引调用

非final类,非final方法

第一次调用因为要生成多个Class对象较]DK慢,但是调用时方法索引较反射方式快

代码框架:

类UserInterface

package com.cocoa.dao;

public interface UserInterface {
    public void test();
}

类UserService

package com.cocoa.dao;

public class UserService implements UserInterface{

    @Override
    public void test() {
        System.out.println("UserService test() -- print");
    }
}

类CGLIBDemo

method.invoke()使用的还是反射机制;但是methodProxy.invoke使用的不是反射,而是FastClass机制,通过建立代理类的索引,快速执行代理的方法。(所以比JDK快)

MethodIntercept的入参:

o:目标对象的实例(被代理的对象);

method:被代理的方法;

objects:方法调用时的入参;

methodProxy:用于调用原始方法的代理。

package com.cocoa.enhancer;

import com.cocoa.dao.UserService;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBDemo {
    public static void Main(String[] args) {
        // 动态代理生成的字节码存储到本地
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "com.cocoa.enhancer");
        final UserService target = new UserService();
        // 增强器
        Enhancer enhancer = new Enhancer();
        // enhancer.setUseCache(false);// 使用缓存
        // 设置代理的类
        enhancer.setSuperclass(UserService.class);
        // 设置代理逻辑
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if (method.getName().equals("test")){

                    System.out.println("before...");
                    method.invoke(target, objects);
                    System.out.println("after...");
                }
                return null;
            }
        });
        // 使用代理类
        UserService userService = (UserService) enhancer.create();// create会将第一次产生的代理类缓存下来
        userService.test();
    }
}

要避免使用method.invoke(),应该使用methodProxy。

类CGLIBDemo1

使用methodProxy.invoke执行方法(通过FastCLass索引机制)

methodProxy.invoke(target, objects);// test() 正常运行,直接执行代理方法

methodProxy.invoke(o, objects);// o 表示代理对象,这样会导致死循环

methodProxy.invokeSuper(target, objects);// CGLIB$test$4() 因为target中没有代理对象的方法

methodProxy.invokeSuper(o, objects);// CGLIB$test$4() 执行代理对象o中的test方法 正常运行

package com.cocoa.enhancer;

import com.cocoa.dao.UserService;
import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * MethodProxy的使用
 */
public class CGLIBDemo1 {
    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "com.cocoa.enhancer");

        final UserService target = new UserService();
        // 增强器
        Enhancer enhancer = new Enhancer();
        // enhancer.setUseCache(false);// 使用缓存
        // 设置代理的类
        enhancer.setSuperclass(UserService.class);
        // 设置代理逻辑
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            // o代理对象 objects入参 method被代理的方法 methodProxy代理的方法
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if (method.getName().equals("test")){
                    System.out.println("before...");
                    // MethodProxy 表示方法代理,代理了两个方法 test()
//                    methodProxy.invoke(target, objects);// test() 可用
//                    methodProxy.invoke(o, objects);// o 表示代理对象,这样会导致死循环
//                    methodProxy.invokeSuper(target, objects);// CGLIB$test$4() 因为target中没有代理对象的方法
                    methodProxy.invokeSuper(o, objects);// CGLIB$test$4() 执行代理对象o中的test方法 可用

                    System.out.println("after...");
                }
                return null;
            }
        });
        // 使用代理类
        UserService userService = (UserService) enhancer.create();// create会将第一次产生的代理类缓存下来
        userService.test();
    }
}

类mainInterface

package com.cocoa.enhancer;

import com.cocoa.dao.UserInterface;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLIB 可以代理接口
 */
public class mainInterface {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        // 设置代理的接口
        enhancer.setSuperclass(UserInterface.class);
        // 设置代理逻辑
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("切面逻辑...");
                return null;
            }
        });
        // 使用代理类
        UserInterface userInterface = (UserInterface) enhancer.create();
        userInterface.test();
    }
}

类JDKDemo

使用proxy.newProxyInstance方法直接构造代理类,入参有:

1)真实对象的类加载器;

2)真实对象实现的接口;

3)代理类需要实现InvocationHandler接口,重写Invoke方法。

invoke方法的入参:

参数1:用Proxy.newProxyInstance方法产生的真实对象,注意,参数1并不显式地出现在方法体内;

参数2:要调用的目标方法;

参数3:目标方法中的参数,一般是  Object[ ]  args。

package com.cocoa.jdkProxy;

import com.cocoa.dao.UserInterface;
import com.cocoa.dao.UserService;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDemo {
    public static void main(String[] args) {
        // Proxy.newproxyInstance
        // 类加载器、代理的接口、new InvocationHandler
        UserService target = new UserService();
        UserInterface userInterface = (UserInterface) Proxy.newProxyInstance(
                JDKDemo.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("test");
                method.invoke(target, args);
                return null;
            }
        });
        userInterface.test();
    }
}

ASM技术尝鲜

通过ASM字节码技术,可以生成一个类。执行下面的代码,就可以生成下图中的类。

package com.cocoa.asmDemo;

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * ASM 尝鲜使用
 */
public class ASMDemo {
    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.AALOAD, 0);// 字节码指令
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        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 zhouyu");
        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( "E:\\java_shicao\\DynamicProxyDemo\\src\\main\\java\\People.class");
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(data);
        fout.close();

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值