软件设计模式(五):代理模式

news2024/12/27 14:03:06

前言

        代理模式是软件设计模式的重中之重,代理模式在实际应用比较多,比如Spring框架中的AOP。在这篇文章中荔枝将会梳理有关静态代理、动态代理的区别以及两种实现动态代理模式的方式。希望能对有需要的小伙伴有帮助~~~


文章目录

前言

一、静态代理

二、动态代理

2.1 基于反射实现动态代理 

2.2 基于cglib实现动态代理

总结


一、静态代理

        静态代理其实比较容易理解,就是我们在扩充一个类方法的功能的时候不想改动原来类的结构,就可以使用代理的方式来实现,这种方式也类似装饰器模式, 代理类还要求和委托类有共同的父类或父接口。 

我们简单来看看一段demo:

        在这段demo中,定义了一个Movable接口并被两个代理类和一个被代理类实现,在测试类中我们执行代理类和实现类中的move()方法。首先会调用TankLogProxy中的move并传入一个TankTimeProxy类对象,再调用TankTimeProxy.move(),之后会触发调用被代理类的move()方法,之后才会执行完之前代理类中剩下的代理方法。

package com.crj.proxy;

import java.util.Random;
/**
 * 静态代理
 */
public class Tank implements Movable {

    /**
     * 模拟坦克移动了一段儿时间
     */
    @Override
    public void move() {
        System.out.println("Tank moving claclacla...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        new TankLogProxy(
                new TankTimeProxy(
                        new Tank()
                )
        ).move();
    }
}

class TankTimeProxy implements Movable {
    Movable m;

    public TankTimeProxy(Movable m) {
        this.m = m;
    }

    @Override
    public void move() {
        long start = System.currentTimeMillis();
        m.move();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

class TankLogProxy implements Movable {
    Movable m;

    public TankLogProxy(Movable m) {
        this.m = m;
    }

    @Override
    public void move() {
        System.out.println("start moving...");
        m.move();
        long end = System.currentTimeMillis();
        System.out.println("stopped!");
    }
}

interface Movable {
    void move();
}

这其实就是静态代理的过程,大家其实可以看到代理类的功能是我们提前写死的,对于不同类型的代理就需要不同的代理类了,这样可能会需要很多很多的代理类。所以我们需要通过一种方式来实现代理类的动态生成。


二、动态代理

        在上文中荔枝提及分离代理行为和代理对象的需求,实现代理类能够代理不同类型的对象,这时候就需要借助动态代理的方式。Java中实现动态代理有两种方式:基于JDK反射机制实现动态代理、借助cglib。

2.1 基于反射实现动态代理 

Java JDK实现动态代理的过程我们可以通过这么一张图来表示,在java.lang.reflect中其实提供了一个Proxy类,我们可以借助该类的newInstance()方法实现动态代理。

我们需要向Proxy.newInstance()方法传入三个参数:类加载对象、接口类和InvocationHandler调用处理对象。Java反射会默认调用InvocationHandler对象中的invoke()方法实现动态代理。

 具体实现动态代理的实例demo如下:

package com.crj.proxy;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;

public class Tank implements Movable {

    /**
     * 模拟坦克移动了一段儿时间
     */
    @Override
    public void move() {
        System.out.println("Tank moving claclacla...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Tank tank = new Tank();

        //reflection 通过二进制字节码分析类的属性和方法

        Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
                new Class[]{Movable.class}, //tank.class.getInterfaces()
                new LogHander(tank)
        );

        m.move();
    }
}

class LogHander implements InvocationHandler {

    Tank tank;

    public LogHander(Tank tank) {
        this.tank = tank;
    }
    //getClass.getMethods[]
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("method " + method.getName() + " start..");
        Object o = method.invoke(tank, args);
        System.out.println("method " + method.getName() + " end!");
        return o;
    }
}



interface Movable {
    void move();
}

        Java JDK反射机制实现动态代理是基于asm类来实现的,该类主要是通过直接修改字节码文件在类加载后直接将代理方法注入字节码文件中,该机制要求被代理类必须实现接口,这也是这种方法实现动态代理的缺点。 

2.2 基于cglib实现动态代理

首先我们需要导入cglib依赖,在项目pom.xml文件中配置

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>

        该方法在Java中提供了一个继承自AbstractClassGenerator类的Enhancer类,在使用动态代理之前必须获得一个Enhancer对象,代理类必须继承一个MethodInterceptor接口并重写intercept方法。在测试类中我们需要设置好被代理对象的父类以及将代理类对象传入setCallback()中,最后调用Enhancer对象的create()方法即可在被代理对象执行方法时实现动态代理。下面给出一个实例demo:

package com.crj.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Random;

/**
 * CGLIB实现动态代理不需要接口
 */
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Tank.class);
        enhancer.setCallback(new TimeMethodInterceptor());
        Tank tank = (Tank)enhancer.create();
        tank.move();
    }
}

class TimeMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println(o.getClass().getSuperclass().getName());
        System.out.println("before");
        Object result = null;
        result = methodProxy.invokeSuper(o, objects);
        System.out.println("after");
        return result;
    }
}

class Tank {
    public void move() {
        System.out.println("Tank moving claclacla...");
        try {
            Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Spring框架中的面向切面编程也是Java动态代理的一部分。


总结

        Proxy模式荔枝已经梳理完成,到现在为止软件设计模式中的最常见的模式和最重点的几个模式已经梳理完成了,收获满满(至少水了几篇文章哈哈哈哈)。总之继续加油,争取明天结束软件设计模式的学习~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

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

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

相关文章

自定义Dynamics 365实施和发布业务解决方案 - 1. 准备工作

在当前的商业世界中,竞争每时每刻都在加剧每个企业都必须找到在竞争中保持领先的直观方法。其中之一企业面临的主要挑战是在以便为客户提供更好的服务。在这样一个竞争激烈、要求苛刻的时代环境中,对客户关系管理软件的需求是正在增加。 Dynamics 365的CE功能强大且适应性强…

使用JS实现一个简单的观察者模式(Observer)

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 手撸Observer⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领…

MySQL数据库——存储引擎(1)-MySQL体系结构、存储引擎简介

目录 MySQL体系结构 连接层 服务层 引擎层 存储层 存储引擎简介 概念 语句 演示 下面开始学习进阶篇的第一个内容——存储引擎 分为四点学习&#xff1a; MySQL体系结构存储引擎简介存储引擎特点存储引擎选择 MySQL体系结构 连接层 最上层是一些客户端和链接服务&am…

小米和金山集团董事长雷军访问武汉:加大投资力度,深化务实合作

小米集团创始人雷军一行在9月6日到访了武汉&#xff0c;受到了当地政府的热情欢迎。武汉方面表示&#xff0c;小米、金山集团作为全球知名的企业集团&#xff0c;与武汉有着良好合作基础。未来&#xff0c;武汉希望小米、金山集团持续深耕武汉&#xff0c;加大投资力度&#xf…

主页整理:8月1日---9月10日

目录 8月1日17点 8月1日20点 8月3日13点 8月3日18点 8月15日19点 8月28日9点 8月28日18点 8月29日8点 8月29日9点 9月2日21点 9月5日17点 9月9日18点 9月10日7点 粉丝变化数 8月1日17点 8月1日20点 8月3日13点 8月3日18点 8月15日19点 8月28日9点 8月28日18点…

Element-ui container常见布局

1、header\main布局 <template> <div> <el-container> <el-header>Header</el-header> <el-main>Main</el-main> </el-container> </div> </template> <style> .el-header { …

日常开发小汇总(3)js类型判断

1.typeof 能判断出字符串、数字、方法和undefined&#xff0c;array、null、object判断不出 let num 1;let str "x";let fn function user(){}let arr [1,2]let obj {name:"zhangs"}let und;let nul null;console.log(typeof num) //numberconsole.l…

深度、广度优先遍历(邻接表)

#include<stdio.h> #include<stdlib.h> #include<iostream> #include<queue> #define MAXVEX 20 typedef char VertexType; using namespace std;//边表结点 typedef struct EdgeNode{int adjvex;struct EdgeNode *next; }EdgeNode;//顶点结点 typedef…

Spring Cloud:构建微服务的最佳实践

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

手机木马远程控制复现

目录 目录 前言 系列文章列表 渗透测试基础之永恒之蓝漏洞复现http://t.csdn.cn/EsMu2 思维导图 1&#xff0c;实验涉及复现环境 2,Android模拟器环境配置 2.1,首先从官网上下载雷电模拟器 2.2,安装雷电模拟器 2.3, 对模拟器网络进行配置 2.3.1,为什么要进行配置…

vagrant 虚拟机扩容磁盘

vagrant 虚拟机扩容磁盘 修改配置安装插件存储扩容 修改配置 参考博客:https://blog.csdn.net/marina_1/article/details/122238721 vagrant 版本 PS D:\vagrant\workplace\node2> vagrant --version Vagrant 2.3.7修改vagrant虚拟机配置文件Vagrantfile&#xff0c;添加磁…

互斥锁,自旋锁,读写锁

目录 互斥体 互斥锁 属性 使用流程&#xff08;相关API函数&#xff09; 互斥锁初始化和销毁的函数原型&#xff1a; 互斥锁的上锁和解锁的函数原型为&#xff1a; pthread_mutex_init() 定义 函数原型 返回值 初始化 pthread_mutex_destroy() 定义 函数原型 pt…

EasyAVFilter代码示例之将视频点播文件转码成HLS(m3u8+ts)视频点播格式

以下是一套完整的视频点播功能开发源码&#xff0c;就简简单单几行代码&#xff0c;就可以完成原来ffmpeg很复杂的视频点播转码调用流程&#xff0c;而且还可以集成在自己的应用程序中调用&#xff0c;例如java、php、cgo、c、nodejs&#xff0c;不需要再单独一个ffmpeg的进程来…

mongodb7.0安装全过程详解

mongodb安装全过程详解 安装mongodb环境安装mongodb shell 安装mongodb环境 进入mongodb官网下载社区版服务器 点击跳转下载地址 选择适应自己系统的版本&#xff0c;然后下载 下图为下载后的文件&#xff0c;双击进行安装 需要注意的两个点 ①选择完整安装 ②不下载mongo…

C语言深入理解指针(非常详细)(五)

目录 回调函数qsort使用举例qsort函数的模拟实现sizeof和strlen的对比sizeofstrlensizeof和strlen的对比一道关于sizeof的题 回调函数 回调函数就是一个通过函数指针调用的函数 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指…

状态管理Pinia

Vue3 状态管理 - Pinia 1. 什么是Pinia Pinia 是 Vue 的专属的最新状态管理库 &#xff0c;是 Vuex 状态管理工具的替代品 2. 手动添加Pinia到Vue项目 后面在实际开发项目的时候&#xff0c;Pinia可以在项目创建时自动添加&#xff0c;现在我们初次学习&#xff0c;从零开始…

DockerFile打包项目实战解析,一文读懂dockerfile打包

文章目录 一、简介二、DockerFile基础1. DockerFile的作用和特点2. DockerFile语法介绍3. 基本指令&#xff1a;FROM、RUN、COPY、ADD等 三、编写具体项目使用DockerFile四、构建镜像1.项目构建默认命令dockerfile文件非默认命令dockerfile文件小知识 2. 使用ENTRYPOINT和CMD指…

C++11 新特性 ⑥ | 智能指针 unique_ptr、shared_ptr 和 weak_ptr

目录 1、引言 2、unique_ptr 3、shared_ptr 4、weak_ptr 5、shared_ptr循环引用问题&#xff08;面试题&#xff09; VC常用功能开发汇总&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xff0c;持续更新...&#xff09;https://blog.csdn.net/chenlycly/article/detai…

Java之List集合的解析及泛型的概述

4.List集合的实现类 4.1List集合子类的特点【记忆】 ArrayList集合 底层是数组结构实现&#xff0c;查询快、增删慢 LinkedList集合 底层是链表结构实现&#xff0c;查询慢、增删快 4.2LinkedList集合的特有功能【应用】 特有方法 方法名说明public void addFirst(E e)在该…

小白备战大厂算法笔试(五)——树

文章目录 二叉树常用术语初始化插入与删除常见类型满二叉树完全二叉树完满二叉树平衡二叉树 二叉树退化二叉树遍历层序遍历前序、中序、后序遍历 数组表示二叉树表示完美二叉树表示任意二叉树 二叉搜索树查找节点插入节点删除节点遍历有序搜索效率常见应用 二叉树 二叉树是一种…