首页 > 其他分享 >JDK和CGLIB动态代理

JDK和CGLIB动态代理

时间:2024-08-01 09:39:32浏览次数:16  
标签:JDK Object 代理 接口 class CGLIB public

一篇写的很好的解释动态代理原理的文章:

博客原地址:https://www.cnblogs.com/lifullmoon/p/14654836.html

代理

代理:在不改变原始代码的情况下修改对象的行为。代理可以以透明的方式为对象添加额外的功能。

言简意赅:方法增强

分类

静态代理

人为编写,编译时就存在

静态代理就是通过实现被代理对象所实现的接口,内部保存了被代理对象,在实现的方法中对处理逻辑进行增强,实际的方法执行调用了被代理对象的方法。

静态代理实现步骤:

  • 定义一个接口及其实现类;

  • 创建一个代理类同样实现这个接口

  • 将目标对象【实现类】注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。

这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。

代码示例:

接口

public interface Email {
    void sendEmail();
}

实现类

public class EmailImpl implements Email{
    @Override
    public void sendEmail() {
        System.out.println("send email");
    }
}

代理类创建

public class EmailProxy implements Email {
    private EmailProxy() {
    }

    private Email email;

    public EmailProxy(Email email) {
        this.email = email;
    }

    @Override
    public void sendEmail() {
        System.out.println("方法开始执行");
        email.sendEmail();
        System.out.println("方法执行结束");
    }
}

测试

public class Test {
    public static void main(String[] args) {
        EmailProxy emailProxy = new EmailProxy(new EmailImpl());
        emailProxy.sendEmail();
    }
}

image-20240801084712166

缺点:

1、接口一旦新增方法,目标对象和代理对象都要进行修改

2、每个代理类只能为一个接口服务,不能为多个接口服务【针对每个目标类都单独创建一个代理类】

​ 假设还有一个Phone的接口,又要再写一个Phone的代理类,麻烦!

3、代理类必须实现接口

动态代理

jdk动态代理

运行时生成代理类,编译时不存在

解决了静态代理每个代理类只能代理一个目标的问题,但是目标对象必须还是要实现接口

代码示例:

代理类:Proxy.newProxyInstance()方法

​ 参数:类加载器、目标对象实现的接口、处理接口方法调用的InvocationHandler处理器

public class JDKProxyFactory {
    private JDKProxyFactory(){}
    public static Object getProxy(Object target){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new MyMethodInterceptor(target));
    }
}

处理器

public class MyMethodInterceptor implements InvocationHandler {
    private final Object target;

    public MyMethodInterceptor(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法开始执行");
        Object res = method.invoke(target, args);
        System.out.println("方法执行结束");
        return res;
    }
}

测试

public class Test {
    public static void main(String[] args) {
        Email email = (Email) JDKProxyFactory.getProxy(new EmailImpl());
        email.sendEmail();

        System.out.println("=================================");

        Phone phon = (Phone) JDKProxyFactory.getProxy(new PhoneImpl());
        phon.call();
    }
}

image-20240801090847107

缺点:只能代理实现了接口的类

为什么 JDK 动态代理只能基于接口代理,不能基于类代理?

在JDK动态代理的底层代码中,对于入参中的 interfaces 如果存在非接口,那么会抛出异常;且从生成的代理对象中看到会继承 Proxy 这个类,在 Java 中类只能是单继承关系,无法再继承一个代理类,所以只能基于接口代理。

CGLIB 动态代理

【天空一声巨响,cglib闪亮登场】

CGLIB 通过继承方式实现代理。

CGLIB 动态代理基于类代理(字节码提升),通过 ASM(Java 字节码的操作和分析框架)将被代理类的 class 文件加载进来,通过修改其字节码生成子类来处理。

很多知名的开源框架都使用到了CGLIB,例如 Spring 中的 AOP 模块中:

如果目标对象实现了接口或者是代理类的子类,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

image-20240801092715724

CGLIB基本介绍:

Code Generation Library代码生成类库,可以动态的给代理类生成一个子类,然后使用继承方式重写被代理方法

CGLIB基本使用

  • 创建增强器对象Enhancer

  • 给增强器对象设置要代理的父类以及拦截器回调

  • 创建方法拦截器类,实现MethodInterceptor接口

  • 创建代理对象,并调用方法

JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。

代码示例:

public class Email1 {
    public void sendEmail()
    {
        System.out.println("发送邮件");
    }
}

创建代理类

public class CglibProxyFactory {
    private CglibProxyFactory(){}

    public static Object getProxy(Class<?> clazz)
    {
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(clazz.getClassLoader());
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new MyCallback());
        return enhancer.create();
    }
}

拦截器回调Callback

因为 MethodInterceptor 继承了 Callback 回调接口,所以可以传入一个 MethodInterceptor 方法拦截器

注意,如果你想设置一个 Callback[] 数组去处理不同的方法,那么需要设置一个 CallbackFilter 筛选器,用于选择这个方法使用数组中的那个 Callback 去处理

public class MyCallback implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("after");
        return result;
    }
}

测试

public class Test {
    public static void main(String[] args) {
        Email1 email = (Email1) CglibProxyFactory.getProxy(Email1.class);
        email.sendEmail();
    }
}

image-20240801093028672

CGLIB 动态代理在 JVM 运行时会新生成一个代理类,这个代理对象继承了目标类,也就是创建了一个目标类的子类,从而字节码得到提升。

标签:JDK,Object,代理,接口,class,CGLIB,public
From: https://www.cnblogs.com/kk-koala/p/18335996

相关文章

  • JDK,JRE和JVM
    1.1JDK,JRE和JVM目录1.1JDK,JRE和JVM1.1.1JDK:JavaDevelopmentKit1.1.2JRE:JavaRuntimeEnvironment1.1.3JVM:JavaVirtualMachine1.1.4图示1.1.1JDK:JavaDevelopmentKitJDK是Java开发工具包,它是一个全面的软件开发工具集合,包含了Java开发所需的所有工具和库。JDK是为......
  • JDK的安装和配置
    1.2JDK的安装和配置目录1.2JDK的安装和配置1.2.1下载JDK1.2.2配置环境变量JAVA_HOME设置PATH设置CLASSPATH设置1.2.3测试JDK是否安装成功1.2.1下载JDK首先我们需要下载java开发工具包JDK,下载地址:https://www.oracle.com/java/technologies/downloads/,在下载页面中......
  • Spring Cloud Gateway 实现 gRpc 代理
    SpringCloudGateway在3.1.x版本中增加了针对gRPC的网关代理功能支持,本片文章描述一下如何实现相关支持.本文主要基于SpringCloudGateway的官方文档进行一个实践练习。有兴趣的可以翻看官方文档。由于Grpc是基于HTTP2协议进行传输的,因此SrpingCloudGateway......
  • umi——设置proxy代理后,请求status一直为500
    参考:umi.jsproxy设置https请求不验证证书--解决SELF_SIGNED_CERT_IN_CHAIN https://blog.csdn.net/u010974701/article/details/86940422 我的情况umi项目,设置proxy代理后,请求status一直为500.umirc.ts:proxy:{'/api':{target:'https://xxx',//服务端域......
  • 由于 OpenAI API 密钥错误,无法使用 Autogen 代理的本地模型
    我正在尝试将Autogen库与本地Huggingface模型一起使用,而不是使用OpenAIAPI。但是,我遇到了一个问题,收到以下错误:OpenAIError:Theapi_keyclientoptionmustbeseteitherbypassingapi_keytotheclientorbysettingtheOPENAI_API_KEYenvironmentvariable......
  • 企业发卡系统源码/带有代理功能发卡平台源码
    源码简介全新企业发卡系统源码,带有代理功能的发卡平台源码,目前应该算是最完美的一款了,亲测可运营。并且多套模板可以切换更新说明:支付界面短链接二维码后台模板等修复及一些细节优化pc用户端后台稍微美化(颜色调整)安卓用户端后台界面UI美化重写,商户头像根据QQ获取Ad......
  • Nginx 如何代理转发传递真实 ip 地址?
    Nginx是一个高性能的反向代理服务器,也是一个非常流行的负载均衡器和HTTP缓存。其轻量级的设计和高并发处理能力使得它广泛应用于各种Web服务中。在使用Nginx作为反向代理服务器时,一个常见的问题是如何在代理转发过程中传递客户端的真实IP地址。默认情况下,Nginx会......
  • jdk的基本知识点
    JavaDevelopmentKit(JDK)是开发Java应用程序所必需的软件包,它包含了编译、调试和运行Java程序所需的各种工具。下面是一些关于JDK的基本知识:JDK组件Java编译器(javac):用于将Java源代码编译成字节码(.class文件)。Java解释器/执行器(java):用于执行编......
  • 适用于 JDK 1.8 的 Spring Boot 的 maven 的 pom.xml 模板
    适用于JDK1.8的SpringBoot的maven的pom.xml模板 <?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:sche......
  • 如何通过✅ IPIDEA代理IP,轻松实现数据采集和市场拓展工作(上)
    如何通过✅IPIDEA代理IP,轻松实现数据采集和市场拓展工作(上)如何通过✅IPIDEA代理IP,轻松实现数据采集和市场拓展工作前言介绍代理IP在跨境电商和数据采集中的重要性一、跨境电商与代理IP代理IP在跨境电商中的作用主要优势:二、爬虫数据采集与代理IP数据爬取痛点如何解决?......