代理模式【静态代理和动态代理实现业务功能扩展】

静态代理

  • 我们在不修改业务的情况下想要给它增加一些功能,这就需要使用代理模式。
  • 我们不会在原有业务上直接修改,为了避免修改导致程序不可逆转的破坏。
  • 三种角色:抽象角色-接口、真实角色-实现类和代理角色-代理类。
  • 真实角色和代理角色继承的是同一个抽象角色接口!

业务接口类 

负责抽象出业务需要的功能。

//抽象业务
public interface UserService {public void add();public void delete();public void update();public void query();
}

业务的实现类

public class UserServiceImpl implements UserService{@Overridepublic void add() {System.out.println("增加了一个用户");}@Overridepublic void delete() {System.out.println("删除了一个用户");}@Overridepublic void update() {System.out.println("修改了一个用户");}@Overridepublic void query() {System.out.println("查询了一个用户");}
}

代理类

假如我们现在要给业务增加一个新的功能-输出日志功能,我们需要通过一个代理类来实现 ,而不是直接在旧业务上修改代码。

我们增加一个 log 方法(拓展功能),我们需要一个 set 方法来使代理能够通过旧的实现类调用旧的业务。


//代理实现增删改查
public class UserServiceProxy implements UserService{UserServiceImpl userService;public void setUserService(UserServiceImpl userService) {this.userService = userService;}@Overridepublic void add() {log("add");userService.add();}@Overridepublic void delete() {log("delete");userService.delete();}@Overridepublic void update() {log("update");userService.update();}@Overridepublic void query() {log("query");userService.query();}public void log(String message){System.out.println("使用了" + message + "方法");}
}

测试

这样我们的只需要给代理设置旧业务的实现类就实现了业务的功能扩展。

public class Client {public static void main(String[] args) {UserServiceImpl service = new UserServiceImpl();UserServiceProxy proxy = new UserServiceProxy();proxy.setUserService(service);proxy.add();}
}

动态代理

  • 动态代理个静态代理角色一样(抽象角色-接口、真实角色-实现类和代理角色-代理类)
  • 动态代理的代理类是通过反射动态生成的!
  • 动态代理按照实现可以分我:基于接口的 和 基于类的动态代理。
    • 基于接口:JDK 动态代理
    • 基于类:cglib
  • 需要了解两个类:Proxy:代理、InvocationHandler:调用处理程序。
  • 一个动态代理类就是一个接口!
  • 一个动态代理类可以代理多个真实角色类,只需要继承同一个业务接口即可。
  • 使用动态代理可以大大减少代码量!因为它使用了反射!

Proxy:

Proxy 一共只有4个方法:

 

 

 InvocationHandler:

InvocationHandler是一个接口!

InvocationHandler 整个类里只有一个方法:invoke方法。 

 动态代理的实现

抽象角色

//抽象业务
public interface UserService {public void add();public void delete();public void update();public void query();
}

真实角色

public class UserServiceImpl implements UserService {@Overridepublic void add() {System.out.println("增加了一个用户");}@Overridepublic void delete() {System.out.println("删除了一个用户");}@Overridepublic void update() {System.out.println("修改了一个用户");}@Overridepublic void query() {System.out.println("查询了一个用户");}
}

代理角色

使用 Object 作为抽象角色,这样这一个代理类就可以当做工具类来给任何真实角色进行功能扩展!

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;//代理处理程序
public class ProxyInvocationHandler implements InvocationHandler {//被代理的接口private Object target;public void setUserService(Object target) {this.target = target;}//生成并得到代理类public Object getProxy(){return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(),this);}//处理代理实例,调用抽象接口的方法,并返回结果@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//动态代理的本质就是使用反射机制实现!fun1();log(method.getName());Object result = method.invoke(target, args);fun2();return result;}public void fun1(){System.out.println("扩展功能1");}public void fun2(){System.out.println("扩展功能2");}public void log(String message){System.out.println("执行了" + message + "方法");}
}

测试

public class Client {public static void main(String[] args) {//真实角色UserServiceImpl userService = new UserServiceImpl();//代理角色ProxyInvocationHandler handler = new ProxyInvocationHandler();//设置要代理的真实对象handler.setUserService(userService);//动态生成代理类UserService proxy = (UserService) handler.getProxy();proxy.add();proxy.delete();}
}

输出结果:

扩展功能1
执行了add方法
增加了一个用户
扩展功能2
扩展功能1
执行了delete方法
删除了一个用户
扩展功能2

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Gtest在ARM平台上的离线搭建(让Gtest编译安装成功之后的可执行文件.so变成ARM下的—ARM aarch64)(实用篇)

编译时自动调用Cunit或者Gtest的静态或者动态库文件说明拷贝Gtest安装包到新目录下根目录下创建build目录并且进行编译检查生成的库文件是否属于ARM架构下的将库文件拷贝到统一的ARM包下面编译时自动调用Cunit或者Gtest的静态或者动态库文件说明 这里之前在usr/local/lib下面安…

自动驾驶MCU 软件架构说明

目录 1 文档... 2 1.1.1 变更历史... 2 1.1.2 Term.. 2 1.1.3 引用文档... 2 2 MCU软件框架图... 3 3 模块介绍... 3 文档 变更历史 版本Version 状态 Status 内容 Contents 日期 Date 撰写 Editor 批准 Approver V0.1 …

MySQL索引详解

索引 在MySQL中,查询方式可以根据访问表数据的方式分为两种:全表扫描和使用索引。 全表扫描(Full Table Scan): 全表扫描是指在查询过程中,MySQL会遍历整个表的每一行来检查满足查询条件的数据。当查询条件…

[JVM] 5. 运行时数据区(2)-- 程序计数器(Program Counter Register)

一、概述 JVM中的程序计数器(Program Counter Register)是对物理PC寄存器的一种抽象模拟。它是一块很小的内存空间,几乎可以忽略不记。也是运行速度最快的存储区域。在 JVM 规范中,每个线程都有它自己的程序计数器,是…

Linux尖刀——shell

目录 知识点 lsblk grep awk tail du df 对新增存储设备的检测与分区 用lsblk查询块设备 用dmesg看内核打印信息 用ls查看新增设备 对rootfs空间使用情况的监控 知识点 首先想要用shell脚本解决一些问题肯定要熟悉linux的命令 lsblk -t或–tree:以…

数据分析师:解读数据背后的故事

数据在当今信息时代中扮演着至关重要的角色,而数据分析师则是解读和发掘数据中隐藏信息的关键人物。作为数据分析师,他们运用统计学、机器学习和数据可视化等技术手段,从海量的数据中提取出有价值的信息和洞察,并将其转化为可供决…

(学习笔记)TCP基础知识

什么是TCP? TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。 面向连接:一定是[一对一]才能连接,不能像UDP协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;可靠的:无论网络链路中出现了…

Appium python 框架

目录 前言 流程 结构 具体说说 run.py 思路 其他模块 前言 Appium是一个开源的移动应用自动化测试框架,它允许开发人员使用多种编程语言(包括Python)来编写自动化测试脚本。Appium框架提供了一套API和工具,可以与移动设备进…

flutter 自适应宽高气泡框,聊天气泡框

先看效果 前言:::: 网上好多气泡框,都让写固定宽高,无法自适应文本内容。 还有的就是通过算child 然后动态计算气泡框宽高,脱裤子💨,放到listview 刷新数据还会丢ui&am…

xss跨站脚本攻击总结

XSS(跨站脚本攻击) 跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets )CSS的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当…

mybatis-plus 支持不同数据源sql切换

mybatis-plus 支持不同数据源sql切换 本篇内容主要讲解的是mybatis-plus 支持不同数据源sql切换 直接上代码 1、结构代码 2、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactI…

用html+javascript打造公文一键排版系统6:三级标题排版

正文中的标题分为四级&#xff0c;文中结构层次序数依次可以用“一、”“&#xff08;一&#xff09;”“1.”“&#xff08;1&#xff09;”标注&#xff1b;一般第一层用黑体字、第二层用楷体字加粗、第三层和第四层用仿宋体字标注。 对于以阿拉伯数字开头、后接英文点号.及…