有哪些类代理的方式

技术有哪些类代理的方式这篇文章主要介绍“有哪些类代理的方式”,在日常操作中,相信很多人在有哪些类代理的方式问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”有哪些类代理的方式”的疑惑有所帮助!

本文主要介绍“有哪些代理方式”。在日常操作中,相信很多人对于有什么样的代理方式都有疑问。边肖查阅了各种资料,整理出简单易用的操作方法,希望能帮助大家解答“有哪些代理方式”的疑惑!接下来,请和边肖一起学习!

00-1010不出意外,你可能只知道两种代理的方式。一个是JDK,另一个是CGLIB。

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

「定义接口」

publicinterfaceIUserApi{

StringqueryUserInfo();

}

「实现接口」

public classuserapiimplesiuserapi {

public stringqueryuserinfo(){ 0

回报‘福哥,微信官方账号:bugstack虫洞Stack |定居,分享,成长,让你和别人都有所收获!’;

}

}

很好!接下来,我们将向这个类方法使用的代理添加一行额外的输出信息。

五种类代理的方式

@测试

publicvoidtest_reflect()

class userapiclazz=userapi . class;

method queryUserInfo=clazz . getmethod(' queryUserInfo ');

object invoke=queryuserinfo . invoke(clazz . NewInstance());

System.out.println(调用);

}

指数:点评:有代理的地方,几乎都会有倒影。它们是一组可以一起使用的功能类。在反射中,您可以调用方法、获取属性、获取注释和其他相关内容。这些可以与下面的类代理相结合,以完成各种框架中的技术场景。

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

publicclassJDKProxy{

publicationstatgetproxy(Classclazz)throwsException {

class loader class loader=thread . currentthread()。getContextClassLoader();

return(T)proxy . new proxyinstance(class loader,newClass[]{clazz},new invocationhandler(){ 0

publicObjectinvoke(对象代理,方法方法,对象

[] args) throws Throwable {
                System.out.println(method.getName() + " 你被代理了,By JDKProxy!");
                return "小傅哥,公众号:bugstack虫洞栈 | 沉淀、分享、成长,让自己和他人都能有所收获!";
            }
        });
    }

}

@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 - 测试结果:小傅哥,公众号:bugstack虫洞栈 | 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */

 


  • 指数:⭐⭐
  • 场景:中间件开发、设计模式中代理模式和装饰器模式应用
  • 点评:这种JDK自带的类代理方式是非常常用的一种,也是非常简单的一种。基本会在一些中间件代码里看到例如:数据库路由组件、Redis组件等,同时我们也可以使用这样的方式应用到设计模式中。

 

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 - 测试结果:小傅哥,公众号:bugstack虫洞栈 | 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */

 


  • 指数:⭐⭐⭐
  • 场景:Spring、AOP切面、鉴权服务、中间件开发、RPC框架等
  • 点评:CGLIB不同于JDK,它的底层使用ASM字节码框架在类中修改指令码实现代理,所以这种代理方式也就不需要像JDK那样需要接口才能代理。同时得益于字节码框架的使用,所以这种代理方式也会比使用JDK代理的方式快1.5~2.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 - 测试结果:小傅哥,公众号:bugstack虫洞栈 | 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */

 


  • 指数:⭐⭐⭐⭐⭐
  • 场景:全链路监控、破解工具包、CGLIB、Spring获取类元数据等
  • 点评:这种代理就是使用字节码编程的方式进行处理,它的实现方式相对复杂,而且需要了解Java虚拟机规范相关的知识。因为你的每一步代理操作,都是在操作字节码指令,例如:
        Opcodes.GETSTATIC
        Opcodes.INVOKEVIRTUAL,除了这些还有小200个常用的指令。但这种最接近底层的方式,也是最快的方式。所以在一些使用字节码插装的全链路监控中,会非常常见。

 

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 - 测试结果:小傅哥,公众号:bugstack虫洞栈 | 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */

 


  • 指数:⭐⭐⭐⭐
  • 场景:AOP切面、类代理、组件、监控、日志
  • 点评:
        Byte Buddy 也是一个字节码操作的类库,但
        Byte Buddy 的使用方式更加简单。无需理解字节码指令,即可使用简单的 API 就能很容易操作字节码,控制类和方法。比起JDK动态代理、cglib,Byte Buddy在性能上具有一定的优势。
        「另外」,2015年10月,Byte Buddy被 Oracle 授予了 Duke's Choice大奖。该奖项对Byte Buddy的“ Java技术方面的巨大创新 ”表示赞赏。

 

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 - 测试结果:小傅哥,公众号:bugstack虫洞栈 | 沉淀、分享、成长,让自己和他人都能有所收获!
 *
 * Process finished with exit code 0
 */

 


  • 指数:⭐⭐⭐⭐
  • 场景:全链路监控、类代理、AOP
  • 点评:
        Javassist 是一个使用非常广的字节码插装框架,几乎一大部分非入侵的全链路监控都是会选择使用这个框架。因为它不想ASM那样操作字节码导致风险,同时它的功能也非常齐全。另外,这个框架即可使用它所提供的方式直接编写插装代码,也可以使用字节码指令进行控制生成代码,所以综合来看也是一个非常不错的字节码框架。

 

总结

有哪些类代理的方式
 
  • 代理的实际目的就是通过一些技术手段,替换掉原有的实现类或者给原有的实现类注入新的字节码指令。而这些技术最终都会用到一些框架应用、中间件开发以及类似非入侵的全链路监控中。
  • 一个技术栈深度的学习能让你透彻的了解到一些基本的根本原理,通过这样的学习可以解惑掉一些似懂非懂的疑问,也可以通过这样技术的拓展让自己有更好的工作机会和薪资待遇。
  • 这些技术学起来并不会很容易,甚至可能还有一些烧脑。但每一段值得深入学习的技术都能帮助你突破一定阶段的技术瓶颈。

到此,关于“有哪些类代理的方式”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/49661.html

(0)

相关推荐

  • 将文件压缩成PNG图像存储方法是什么

    技术将文件压缩成PNG图像存储方法是什么本篇内容介绍了“将文件压缩成PNG图像存储方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅

    攻略 2021年11月18日
  • school的音标,音标中长短音单词怎么区别

    技术school的音标,音标中长短音单词怎么区别因为相近,所以难辨school的音标。不是长短音的问题,发音长短不是区别的主要方面。长短音只是不负责任的简化表达。iː和ɪ,还有uː和ʊ的发音的区别,根本不是发音长短的问题

    生活 2021年10月19日
  • 厘米的符号,cm这是数学的什么单位

    技术厘米的符号,cm这是数学的什么单位数学单位mm,cm代表毫米和厘米。 毫米,英文缩写mm(或 毫米,又称公厘(或公釐),是长度单位和降雨量单位,英文缩写mm(或 毫米。1毫米相当于1米的一千分之一(此即为毫的字义)。

    生活 2021年10月20日
  • dna聚合酶作用部位,DNA聚合酶作用部位及功能

    技术dna聚合酶作用部位,DNA聚合酶作用部位及功能DNA聚合酶作用部位是磷酸二酯键dna聚合酶作用部位。 1、聚合作用:在引物RNA-OH末端,以dNTP为底物,按模板DNA上的指令,即A与T,C与G的配对原则,逐步逐

    生活 2021年10月24日
  • 试用期工作总结范文,汽车钣金转正工作总结范文

    技术试用期工作总结范文,汽车钣金转正工作总结范文汽车钣金转正工作总结,应该先写下通过这段实习期自己学到的专业技术试用期工作总结范文。然后对自己的同事表示感谢,最后可以表示对公司领导的感谢。范文:汽车钣金工转正申请时间过得

    生活 2021年10月31日
  • vue 组件对外暴露方法(vue 中的store如何存取数据)

    技术Vue中怎样把数据包装成reactive从而实现MDV效果Vue中怎样把数据包装成reactive从而实现MDV效果,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来

    攻略 2021年12月25日