大数据Java

除了JDK、CGLIB,还有3种类代理方式?面试又卡住

2020-10-24  本文已影响0人  热衷技术的Java程序员

五种类代理的方式

不出意外,你可能只知道两种类代理的方式。一种是JDK自带的,另外一种是CGLIB。

我们先定义出一个接口和相应的实现类,方便后续使用代理类在方法中添加输出信息。

「定义接口」

public interface IUserApi {

    String queryUserInfo();

}

「实现接口」

public class UserApi implements IUserApi {

    public String queryUserInfo() {
        return "沉淀、分享、成长,让自己和他人都能有所收获!";
    }

}

好!接下来我们就给这个类方法使用代理加入一行额外输出的信息。

0. 先补充一点反射的知识

@Test
public void test_reflect() throws Exception {
    Class<UserApi> clazz = UserApi.class;
    Method queryUserInfo = clazz.getMethod("queryUserInfo");
    Object invoke = queryUserInfo.invoke(clazz.newInstance());
    System.out.println(invoke);
}


1. JDK代理方式

public class JDKProxy {

    public static <T> T getProxy(Class clazz) throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        return (T) Proxy.newProxyInstance(classLoader, new Class[]{clazz}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName() + " 你被代理了,By JDKProxy!");
                return "沉淀、分享、成长,让自己和他人都能有所收获!";
            }
        });
    }

}

@Test
public void test_JDKProxy() throws Exception {
    IUserApi userApi = JDKProxy.getProxy(IUserApi.class);
    String invoke = userApi.queryUserInfo();
    logger.info("测试结果:{}", invoke);
}

/**
 * 测试结果:
 * 
 * queryUserInfo 你被代理了,By JDKProxy!
 * 19:55:47.319 [main] INFO  org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */


2. CGLIB代理方式

public class CglibProxy implements MethodInterceptor {
    public Object newInstall(Object object) {
        return Enhancer.create(object.getClass(), this);
    }
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("我被CglibProxy代理了");
        return methodProxy.invokeSuper(o, objects);
    }
}

@Test
public void test_CglibProxy() throws Exception {
    CglibProxy cglibProxy = new CglibProxy();
    UserApi userApi = (UserApi) cglibProxy.newInstall(new UserApi());
    String invoke = userApi.queryUserInfo();
    logger.info("测试结果:{}", invoke);
}

/**
 * 测试结果:
 * 
 * queryUserInfo 你被代理了,By CglibProxy!
 * 19:55:47.319 [main] INFO  org.itstack.interview.test.ApiTest - 测试结果:  沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */


3. ASM代理方式

public class ASMProxy extends ClassLoader {

    public static <T> T getProxy(Class clazz) throws Exception {

        ClassReader classReader = new ClassReader(clazz.getName());
        ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);

        classReader.accept(new ClassVisitor(ASM5, classWriter) {
            @Override
            public MethodVisitor visitMethod(int access, final String name, String descriptor, String signature, String[] exceptions) {

                // 方法过滤
                if (!"queryUserInfo".equals(name))
                    return super.visitMethod(access, name, descriptor, signature, exceptions);

                final MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);

                return new AdviceAdapter(ASM5, methodVisitor, access, name, descriptor) {

                    @Override
                    protected void onMethodEnter() {
                        // 执行指令;获取静态属性
                        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
                        // 加载常量 load constant
                        methodVisitor.visitLdcInsn(name + " 你被代理了,By ASM!");
                        // 调用方法
                        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
                        super.onMethodEnter();
                    }
                };
            }
        }, ClassReader.EXPAND_FRAMES);

        byte[] bytes = classWriter.toByteArray();

        return (T) new ASMProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance();
    }

}

@Test
public void test_ASMProxy() throws Exception {
    IUserApi userApi = ASMProxy.getProxy(UserApi.class);
    String invoke = userApi.queryUserInfo();
    logger.info("测试结果:{}", invoke);
}

/**
 * 测试结果:
 * 
 * queryUserInfo 你被代理了,By ASM!
 * 20:12:26.791 [main] INFO  org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */


4. Byte-Buddy代理方式

public class ByteBuddyProxy {

    public static <T> T getProxy(Class clazz) throws Exception {

        DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
                .subclass(clazz)
                .method(ElementMatchers.<MethodDescription>named("queryUserInfo"))
                .intercept(MethodDelegation.to(InvocationHandler.class))
                .make();

        return (T) dynamicType.load(Thread.currentThread().getContextClassLoader()).getLoaded().newInstance();
    }

}

@RuntimeType
public static Object intercept(@Origin Method method, @AllArguments Object[] args, @SuperCall Callable<?> callable) throws Exception {
    System.out.println(method.getName() + " 你被代理了,By Byte-Buddy!");
    return callable.call();
}

@Test
public void test_ByteBuddyProxy() throws Exception {
    IUserApi userApi = ByteBuddyProxy.getProxy(UserApi.class);
    String invoke = userApi.queryUserInfo();
    logger.info("测试结果:{}", invoke);
}

/**
 * 测试结果:
 * 
 * queryUserInfo 你被代理了,By Byte-Buddy!
 * 20:19:44.498 [main] INFO  org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */


5. Javassist代理方式

public class JavassistProxy extends ClassLoader {

    public static <T> T getProxy(Class clazz) throws Exception {

        ClassPool pool = ClassPool.getDefault();
        // 获取类
        CtClass ctClass = pool.get(clazz.getName());
        // 获取方法
        CtMethod ctMethod = ctClass.getDeclaredMethod("queryUserInfo");
        // 方法前加强
        ctMethod.insertBefore("{System.out.println(\"" + ctMethod.getName() + " 你被代理了,By Javassist\");}");

        byte[] bytes = ctClass.toBytecode();

        return (T) new JavassistProxy().defineClass(clazz.getName(), bytes, 0, bytes.length).newInstance();
    }

}

@Test
public void test_JavassistProxy() throws Exception {
    IUserApi userApi = JavassistProxy.getProxy(UserApi.class)
    String invoke = userApi.queryUserInfo();
    logger.info("测试结果:{}", invoke);
}

/**
 * 测试结果:
 * 
 * queryUserInfo 你被代理了,By Javassist
 * 20:23:39.139 [main] INFO  org.itstack.interview.test.ApiTest - 测试结果: 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */


四、总结

除了JDK、CGLIB,还有3种类代理方式?面试又卡住

</article>

上一篇 下一篇

猜你喜欢

热点阅读