Java动态代理与静态代理

代理模式

在Java中有多达23种的设计模式(后面Java基础更新完后,会找个时间详细的去写写这些设计模式),恰当的设计模式的使用能够提升代码的效率,简化代码的复杂性。

而今天我们要说的代理模式就是其中之一,所谓代理是为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

大白话:买房的(客户方),房产销售(代理方),卖房的(委托方)

在Java中有静态代理和动态代理两种实现方式,继续放下看!!!

静态代理

所谓静态代理,一般是针对编译期就已经完成了接口,实现类,代理类的定义,我们对目标对象的增强需要手工去完成,一个目标对象就要有个代理类,非常不灵活。

静态代理的实现步骤

1,因为代理类与被目标对象有相似的行为(共同),所以我们先创建一个接口。

public interface SaleHouse {
     String saleHouse();
}

2,提供接口的实现类,当做目标对象

public class SaleHouseImpl implements SaleHouse{
    @Override
    public String saleHouse() {
        return "我要卖房子啦!!!";
    }
}

3,代理类同样也要实现接口,并在目标方法前后做一些控制操作

public class SaleHouseProxy implements SaleHouse{

    private SaleHouse saleHouse;
	//提供一个包含目标对象的有参构造
    public SaleHouseProxy(SaleHouse saleHouse) {
        this.saleHouse = saleHouse;
    }

    @Override
    public String saleHouse() {
        //调用方法前,我们可以加一些自己的控制
        System.out.println("我要收代理费!!!");
        System.out.println("--------------------");
        String s = saleHouse.saleHouse();
        System.out.println(s);
        System.out.println("--------------------");
        //调用方法后,我们依旧可以操作
        System.out.println("我要拿提成!!!");
        return "这就是静态代理";
    }
}

4,客户端调用代理类,并传入目标对象

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
        //客户端调用静态代理
        SaleHouse saleHouse = new SaleHouseImpl();
        SaleHouseProxy saleHouseProxy = new SaleHouseProxy(saleHouse);
        saleHouseProxy.saleHouse();
    }
}

5,控制台查看打印结果

我要收代理费!!!
--------------------
我要卖房子啦!!!
--------------------
我要拿提成!!!

动态代理

其实无论是静态代理还是静态代理,在我们的日常开发中,使用的都是很多,但对于SpringAop、RPC等框架来说,动态代理发挥着相当大的作用,动态代理具有:运行时控制,灵活性更好的特点。
那怎么实现动态代理呢?
如下三种方式:

JDK 动态代理
CGLib 动态代理
使用 Spring aop 模块完成动态代理功能 //今天先不说这个

JDK动态代理

实现步骤:
1,定义一个接口及其实现类;
代码同静态代理中步骤1,步骤2;

2,自定义 InvocationHandler (调用处理器)并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;

public class JdkDynamicInvocationHandler implements InvocationHandler {

    //代理类中的真实对象
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用方法前,我们可以加一些自己的控制
        System.out.println("我要收代理费!!!");
        Object invoke = method.invoke(target, args);
        //调用方法后,我们依旧可以操作
        System.out.println("我要拿提成!!!");
        return invoke;
    }
}

3,通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;其实,这一步也可以写在第2步的代码里,不过为了代码的可读性,我们进行解耦实现!
3.1,定义一个工厂类,在工厂类中通过Proxy.newProxyInstance()方法获取某个类的代理对象

public class JdkDynamicProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 目标类的类加载器
            target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
            new JdkDynamicInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
        );
    }

3.2,客户端传入目标对象,实现代理扩展

  //客户端调用静态代理
SaleHouse proxySaleHouse = (SaleHouse) JdkDynamicProxyFactory.getProxy(new SaleHouseImpl());
proxySaleHouse.saleHouse();

4,控制台输出

我要收代理费!!!
我要卖房子啦!!!
我要拿提成!!!

【扩展】
关于Proxy类的静态工厂方法newProxyInstance()如何创建代理实例的过程,感兴趣的可以去读源码,或者参考下面这篇文章《代理模式在开源代码中的应用》

CGLIB 动态代理

其实在JDK动态代理中有一个弊端,那就是只能代理接口或接口的实现类,那么未实现任何接口的类就不能代理了吗?答案是否定的,因为咱们有CGLIB!

CGLIB (Code Generation Library) 是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,CGLIB 通过继承方式实现代理。

实现步骤:
1,引入cglib依赖
因为是第三方实现的动态代理,所以在使用前先引入依赖包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

2,定义一个类;

public class Person {
    public void eat(){
        System.out.println("我在吃饭!!!");
    }
}

3,自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;

public class CglibMethodInterceptor implements MethodInterceptor {

    /**
     * @param o           被代理的对象(需要增强的对象)
     * @param method      被拦截的方法(需要增强的方法)
     * @param args        方法入参
     * @param methodProxy 用于调用原始方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //调用方法前,我们可以加一些自己的控制
        System.out.println("饭前先洗手");
        Object object = methodProxy.invokeSuper(o, args);
        //调用方法前,我们可以加一些自己的控制
        System.out.println("饭后要擦嘴");
        return object;
    }
}

4,创建一个工厂类,用来构建代理对象,通过 Enhancer 类的 create()方法实现;

public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new CglibMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}

5、客户端调用,通过反射传入Person类信息

public static void main(String[] args) throws FileNotFoundException {
        //客户端调用静态代理
        Person person = (Person) CglibProxyFactory.getProxy(Person.class);
        person.eat();
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/368423.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

车载以太网:PHY(物理层)介绍

0 工具准备 TJA1101B芯片手册 TJA1101B automotive Ethernet PHY手册 IEEE802.3-2018.pdf 1 车载以太网PHY&#xff08;物理层&#xff09;介绍 常见的普通以太网分为10BASE-2、10/100BASE-TX和1000BASE-T&#xff0c;一般都使用RJ45接口&#xff0c;对于1000BASE-T来说&#…

数据结构中的时间复杂度和空间复杂度基础

目录 数据结构 数据结构中的基本名词 数据 数据对象 数据元素 数据项 数据类型 数据对象、数据元素和数据项之间的关系 数据结构及分类 逻辑结构 物理结构 算法 算法的特点 算法设计上的要求 算法效率的衡量 时间复杂度 大O渐进表示法 最坏情况和平均情况 常…

数字化转型:企业适应新常态的关键之举_光点科技

在全球商业环境不断演变和技术日新月异的背景下&#xff0c;数字化转型已经成为企业不可回避的课题。它不仅关乎企业的未来生存与发展&#xff0c;更是适应新常态、提升竞争力的关键之举。但是&#xff0c;数字化转型并非一夜之间可以完成的任务&#xff0c;它需要全面的策略规…

面试数据结构与算法总结分类+leetcode题目目录【基础版】

&#x1f9e1;&#x1f9e1;&#x1f9e1;算法题目总结&#xff1a; 这里为大家总结数据结构与算法的题库目录&#xff0c;如果已经解释过的题目会标注链接更新&#xff0c;方便查看。 数据结构概览 Array & String 大家对这两类肯定比较清楚的&#xff0c;同时这也是面试…

2024022期传足14场胜负前瞻

2024022期赛事由英超4场&#xff0c;德甲2场、意甲4场、西甲4场组成。售止时间为2月4日&#xff08;周日&#xff09;19点00分&#xff0c;敬请留意&#xff1a; 本期中深盘较多&#xff0c;1.5以下赔率3场&#xff0c;1.5-2.0赔率7场&#xff0c;其他场次是平半盘、平盘。本期…

【C++】拷贝构造函数和赋值运算符重载详解

目录 拷贝构造函数 概念 特征 赋值运算符重载 运算符重载 赋值运算符重载 ​编辑前置和后置重载 ⭐拷贝构造函数 ⭐概念 拷贝构造函数&#xff1a;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存 在的类类型对象创建新…

AJAX-常用请求方法和数据提交

常用请求方法 请求方法&#xff1a;对服务器资源&#xff0c;要执行的操作 axios请求配置 url&#xff1a;请求的URL网址 method&#xff1a;请求的方法&#xff0c;如果是GET可以省略&#xff1b;不用区分大小写 data&#xff1a;提交数据 axios({url:目标资源地址,method…

初始mach-o文件及在项目中应用

本文字数&#xff1a;2250字 预计阅读时间&#xff1a;15分钟 01 认识mach-o的必要性 了解mach-o的结构可以帮助认识系统加载二进制文件的动态链接和静态链接。应用层面&#xff0c;使用initialize的c函数计算启动时间耗时也需要以mach-o的结构知识为铺垫。还可以用在使用clang…

css绘制向左三角形_纯css 实现三角形

首先这个思路的讲解 就是用到了 border 边框这个属性 一个div 可以设置四边边框 我们先把其他三条边都去掉 只留下一个边框 其他 在设置底边框的宽度 再把内容区域设置为0 就可以了 下面是代码 <div></div>div {width:0;height:0;border-top:30px solid red ;bor…

机器学习——集成学习

&#x1f4d5;参考&#xff1a;ysu老师课件西瓜书 期末复习笔记 1.集成学习的基本概念 集成学习&#xff08;ensemble learing&#xff09;通过构建并结合多个学习器来完成学习任务。 有时也被称为多分类器系统&#xff08;multi-classifier system&#xff09;、基于委员会的…

Flink实时数仓同步:快照表实战详解

一、背景 在大数据领域&#xff0c;初始阶段业务数据通常被存储于关系型数据库&#xff0c;如MySQL。然而&#xff0c;为满足日常分析和报表等需求&#xff0c;大数据平台采用多种同步方式&#xff0c;以适应这些业务数据的不同存储需求。这些同步存储方式包括离线仓库和实时仓…

1894_透明性以及可显性

1894_透明性以及可显性 全部学习汇总&#xff1a; g_unix: UNIX系统学习笔记 (gitee.com) 这是一个透明性以及可显性的基本描述。 这个是对透明性以及可显性的功能作用的一个基本描述。 做一个简单的小结&#xff0c;从一个软件工程师的角度来看看透明性以及可显性的概念和作用…

[职场] 资信评估是什么? #学习方法#笔记

资信评估是什么&#xff1f; 资信评估是指对个人或机构的信用状况进行评估和分析的过程。它主要通过收集和分析各种相关信息来评估借款人或机构的还款能力、信用历史和风险承受能力&#xff0c;以确定其信用等级或信用评分。 资信评估通常包括以下几个方面的内容&#xff1a; …

2024年【道路运输企业主要负责人】新版试题及道路运输企业主要负责人考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业主要负责人考试报名是安全生产模拟考试一点通总题库中生成的一套道路运输企业主要负责人理论考试&#xff0c;安全生产模拟考试一点通上道路运输企业主要负责人作业手机同步练习。2024年【道路运输企业主…

gif动图的裁剪实现思路

项目需求(对app的轮播,以及banner和咨询的图片进行裁剪):前期实现使用用vue-cropper插件对图片进行插件,----后续需求需要裁剪gif动图(vue-cropper、微信自带的截图工具,以及fastStone截图工具,都只能截取静态图片,打开动图时只显示某一帧的静态图片),所以需要研究为什么vue-cr…

Blender 与 3ds Max | 面对面的直接较量(2024)

Blender和3ds Max&#xff0c;哪个动画软件更好&#xff1f;作为一个从事动画领域十年的专业人士&#xff0c;Mark McPherson提供了八条最新建议&#xff0c;帮助你了解哪个软件更适合满足你的3D动画需求。 1.建模 获胜者&#xff1a;3ds Max。3ds Max的建模机制已经被证明是…

uniapp H5 px转换rpx

uniapp H5 px转换rpx 安装 px2rpx 重启 HBuilderX在要转换的文件 点击右键 点击 开启px2rpx(1px转成2rpx) 开启成功&#xff01;使用 编辑页面后 按下键盘 Ctrl s 保存&#xff01;转化成功&#xff01;当然 你也需要对使用的插件 进行转换&#xff01;否则可能导致样式出现…

xinput1_3.dll文件缺失的解决方法

在使用计算机的过程中&#xff0c;遇到“找不到xinput1_3.dll”这样的错误提示并不罕见。该问题通常出现在尝试启动某些游戏或应用程序时&#xff0c;特别是那些依赖于Microsoft DirectX接口来处理输入设备&#xff08;如游戏手柄&#xff09;的程序。xinput1_3.dll是DirectX库…

Fink CDC数据同步(二)MySQL数据同步

1 开启binlog日志 2 数据准备 use bigdata; drop table if exists user;CREATE TABLE user(id INTEGER NOT NULL AUTO_INCREMENT,name VARCHAR(20) NOT NULL DEFAULT ,birth VARCHAR(20) NOT NULL DEFAULT ,gender VARCHAR(10) NOT NULL DEFAULT ,PRIMARY KEY(id) ); ALTER TA…