静态代理和动态代理的区别以及实现过程

news2025/4/15 2:59:13

前言

代理模式是一种设计模式,能够使得在不修改源目标的前提下,额外扩展源目标的功能。即通过访问源目标的代理类,再由代理类去访问源目标。这样一来,要扩展功能,就无需修改源目标的代码了。只需要在代理类上增加就可以了。

在这里插入图片描述
其实代理模式的核心思想就是这么简单,在java中,代理又分静态代理动态代理2种,其中动态代理根据不同实现又区分基于接口的的动态代理和基于子类的动态代理。

其中静态代理由于比较简单,面试中也没啥问的,在代理模式一块,问的最多就是动态代理,而且动态代理也是spring aop的核心思想,spring其他很多功能也是通过动态代理来实现的,比如拦截器,事务控制等。

熟练掌握动态代理技术,能让你业务代码更加精简而优雅。如果你需要写一些中间件的话,那动态代理技术更是必不可少的技能包。

静态代理

静态代理,就是通过声明一个明确的代理类来访问源对象。

我们有1个接口,Person。这个个接口各有2个实现类,UML如下图:
在这里插入图片描述

实现

接口:person.java

package StaticProxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:29
 * @Description:
 */
public interface Person {

    /**
     * 起床
     */
    public void wakeup();

    /**
     * 睡觉
     */
    public void sleep();
}

实现类:Student .java

package StaticProxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:32
 * @Description:
 */
public class Student implements Person{
    private String name;

    public Student(){
    }

    public Student(String name){
        this.name = name;
    }


    @Override
    public void wakeup() {
        System.out.println("学生:"+name+",起床了!!!");
    }

    @Override
    public void sleep() {
        System.out.println("学生:"+name+",睡觉了!!!");
    }
}

假设我们现在要做一件事,就是在所有的实现类调用wakeup()前增加一行输出早安,调用sleep()前增加一行输出晚安。那我们只需要编写1个代理类PersonProxy

代理类:PersonProxy .java

package StaticProxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:35
 * @Description:
 */
public class PersonProxy implements Person{
    private Person person;

    public PersonProxy(Person person){
        this.person = person;
    }
    @Override
    public void wakeup() {
        System.out.println("早上好啊!!!");
        person.wakeup();
    }

    @Override
    public void sleep() {
        System.out.println("晚上好啊!!!");
        person.sleep();
    }
}

测试类

package StaticProxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:37
 * @Description:
 */
public class Test {
    public static void main(String[] args) {
        Person student1 = new Student("张三");
        PersonProxy studentProxy = new PersonProxy(student1);
        studentProxy.wakeup();
        studentProxy.sleep();
    }
}

结果

在这里插入图片描述

结论:

静态代理的代码相信已经不用多说了,代码非常简单易懂。这里用了1个代理类,代理了Person接口。

这种模式虽然好理解,但是缺点也很明显:

  • 会存在大量的冗余的代理类,这里演示了1个接口,如果有10个接口,就必须定义10个代理类。
  • 不易维护,一旦接口更改,代理类和目标类都需要更改。

动态代理

动态代理,通俗点说就是:无需声明式的创建java代理类,而是在运行过程中生成"虚拟"的代理类,被ClassLoader加载。从而避免了静态代理那样需要声明大量的代理类。

JDK从1.3版本就开始支持动态代理类的创建。主要核心类只有2个:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

还是前面那个例子,用JDK动态代理类去实现的代码如下:

创建一个JdkProxy类,用于统一代理:

package DynamicProxy;

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

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:28
 * @Description:
 */
public class JdkProxy implements InvocationHandler {

    private Object bean;

    public JdkProxy(Object bean) {
        this.bean = bean;
    }

    /**
     * 其中proxy为代理过之后的对象(并不是原对象),method为被代理的方法,args为方法的参数。
     *
     * 如果你不传原有的bean,直接用method.invoke(proxy, args)的话,那么就会陷入一个死循环。
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();

        if (methodName.equals("wakeup")) {
            System.out.println("早安~~~");
        } else if (methodName.equals("sleep")) {
            System.out.println("晚安~~~");
        }

        return method.invoke(bean, args);
    }
}

测试

package DynamicProxy;

import StaticProxy.Person;
import StaticProxy.Student;

import java.lang.reflect.Proxy;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 13:46
 * @Description:
 */
public class Test {
    public static void main(String[] args) {
        JdkProxy proxy = new JdkProxy(new Student("李四"));
        Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);
        student.wakeup();
        student.sleep();
    }
}

结果

在这里插入图片描述

可以看到,相对于静态代理类来说,无论有多少接口,这里只需要一个代理类。核心代码也很简单。唯一需要注意的点有以下2点:

JDK动态代理是需要声明接口的,创建一个动态代理类必须得给这个”虚拟“的类一个接口。可以看到,这时候经动态代理类创造之后的每个bean已经不是原来那个对象了。

这里JdkProxy最核心的方法就是

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable

其中proxy为代理过之后的对象(并不是原对象),method为被代理的方法,args为方法的参数。

如果你不传原有的bean,直接用method.invoke(proxy, args)的话,那么就会陷入一个死循环。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《真象还原》读书笔记——第五章 保护模式进阶,向内核迈进(特权级,更新)

5.4 特权级深入浅出 5.4.1 特权级哪点事 计算机 访问 可分为访问者和被访问者。 建立特权机制为了通过特权来检查合法性。 0、1、2、3级,数字越小,权力越大。 0特权级是系统内核特权级。 用户程序是3特权级,被设计为“有需求就找操作系统”…

QT+OpenGL光照

QTOpenGL光照 本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主 颜色 现实生活中看到的物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的颜色 太阳光能被看见的白光是多找演的的组合…

Linux部署项目

一、手动部署 1、终端直接部署(前台运行) (1)在IDEA中开发SpringBoot项目并打成jar包 (2)将jar包上传到Linux服务器 mkdir /usr/local/app #创建目录,将项目jar包放到此目录(3&…

C++——哈希4|布隆过滤器

目录 布隆过滤器 完整代码 布隆过滤器应用 布隆过滤器的查找 布隆过滤器删除 布隆过滤器优点 布隆过滤器缺陷 布隆过滤器海量数据处理 布隆过滤器 位图只能映射整形,而对于字符串却无能为力。 把字符串用哈希算法转成整形,映射一个位置进行标…

Axure教程(一)——线框图与高保真原型图制作

前面我们学习了制作网页的技能,从这里开始我们来学习前端必备技能,就是用Axure来制作原型图,一方面我们能提前绘制出我们所需的页面,这在我们开发的时候能节省大量的时间,另一方面我们能通过给用户进行体验从而能够发现…

robotframework + selenium自动化测试常见的问题

1、 插入中文数据提示 FAIL UnicodeEncodeError: ‘latin-1’ codec can’t encode characters in position 92-107: ordinal not in range(25 DataBaseLibrary插入中文乱码的解决:修改D:\Python27\Lib\site-packages\DatabaseLibrary\connection_manager.py里的co…

极客大挑战 2021

题量很大,收获挺多,持续时间也长,据说结束之后会再持续一段时间,然后题目会开源。 WEB Dark 暗网签到,难以置信 Welcome2021 改个请求方法会提示你文件,再进去就好了 babysql 直接把请求包扔sqlmap里&…

什么是仓库管理?

仓库管理包括仓库日常运营所触及的准绳和流程。从较高的层次上讲,这包括接纳和组织仓库空间、布置劳动力、管理库存和完成订单。放大来看,你会发现有效的仓库管理触及到优化和集成这些过程中的每一个,以确保仓库操作的一切方面协同工作&#…

ElasticSearch之RestClient操作索引库和文档

前言:上文介绍了使用DSL语言操作索引库和文档,本篇文章将介绍使用Java中的RestClient来对索引库和文档进行操作。 希望能够加深自己的印象以及帮助到其他的小伙伴儿们😉😉。 如果文章有什么需要改进的地方还请大佬不吝赐教&#x…

linux系统编程1--文件编程read和write

write函数进行文件写入操作1.write函数原型:ssize_t write(int fd, const void *buf, size_t count);2.参数1:int fd,文件描述符即open函数的返回值;参数2:const void *buf,写入到fd文件内容的写入缓冲区&a…

10万字企业数字化(技术中台、数据中台、工业互联网平台建设方案

【版权声明】本资料来源网络,知识分享,仅供个人学习,请勿商用。【侵删致歉】如有侵权请联系小编,将在收到信息后第一时间删除!完整资料关注公众号“智慧方案文库”,部分资料内容: 目录 1 概述 …

【nodejs-05】黑马nodejs学习笔记05-数据库基本操作02

文章目录4.在项目中操作MySQL4.1 在项目中操作数据库的步骤4.2 安装与配置 mysql 模块4.3 使用 mysql 模块操作 MySQL 数据库5.前后端的身份认证5.1 Web 开发模式5.2 身份认证5.3 Session 认证机制5.4 在 Express 中使用 Session 认证5.5 JWT 认证机制5.6 在 Express 中使用 JW…

用类比方式学习编程中函数递归(个人理解仅供参考)(内含汉诺塔问题的求解)

目录 1.前言 2.递归的数学模型 3.相关的c语法 4.将递归的数学模型写成编程语言 5.利用类比方法将实际问题的代码写成函数递归的形式 例1: 例2: 6.汉诺塔问题的求解 1.前言 本人在学习函数递归编程方法的过程中,发现用类比的方式学习递归法可帮助我们在各种编…

day14_类中成员之一:构造器

由来 我们发现我们new完对象时,所有成员变量都是默认值,如果我们需要赋别的值,需要挨个为它们再赋值,太麻烦了。我们能不能在new对象时,直接为当前对象的某个或所有成员变量直接赋值呢。可以,Java给我们提…

【算法】二分

作者:指针不指南吗 专栏:算法篇 🐾或许会很慢,但是不可以停下来🐾 文章目录1.二分思想2.二分模板3.二分应用1.二分思想 思想 单调的元素,一定可以二分;非单调不一定不能二分 每次把整个区间 [ …

【企业云端全栈开发实践-1】项目介绍及环境准备、Spring Boot快速上手

本节目录一、 项目内容介绍二、Maven介绍2.1 Maven作用2.2 Maven依赖2.3 本地仓库配置三、Spring Boot快速上手3.1 Spring Boot特点3.2 遇到的Bug:spring-boot-maven-plugin3.3 遇到的Bug2:找不到Getmapping四、开发环境热部署一、 项目内容介绍 本课程…

肿瘤HRR和HRD 简单记录

最近看到两个在肿瘤领域高频出现的词HRR和HRD, 遂简单记录下。 HRR和HRD的概念 当细胞受到外界不良环境的压力下往往会导致DNA的损伤,此时便会触发DNA 损伤反应 (DDR),从而激活许多DNA修复通路。在这些DNA损伤中,DNA 双链断裂&a…

【云原生】centos7搭建安装k8s集群 v1.25版本详细教程实战

文章目录前言一. 实验环境二. k8s 的介绍三 . k8s的安装3.1 搭建实验环境3.1.1 硬件层面的要求3.1.2 软件层面环境配置3.2 docker的安装3.2.1 搭建docker3.2.2 部署 cri-dockerd3.3 部署k8s3.3.1 配置添加阿里云的yum源3.3.2 安装kubeadm kubelet kubectl3.3.3 k8s-master节点初…

浪涌保护器,防雷浪涌保护器的作用和类型指南

1. 什么是SPD浪涌保护器?地凯防雷SPD浪涌保护器是防止雷击导致故障的避雷器和浪涌保护设备。广泛用于电源浪涌对策的变阻器在通电超过规格的雷电浪涌电流、超过最大容许电路电压的过电压、过电流时,会进入短路故障模式,存在冒烟起…

Mysql元数据获取方法(information_schema绕过方法)

前提:如果waf或其它过滤了information_schema关键字,那我们该如何获取元数据呢?能够代替information_schema的有:sys.schema_auto_increment_columnssys.schema_table_statistics_with_bufferx$schema_table_statistics_with_buff…