Java设计模式之(五)——代理模式
1、什么是代理模式
Provide a surrogate or placeholder for another object to control access to it.
Proxy Pattern:为其他对象提供一种代理以控制对这个对象的访问。
说人话:在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能,比如Spring AOP。
2、代理模式定义
①、Subject
抽象主题角色,可以是抽象类,可以是接口,是一个最普通的业务类定义,无特殊要求。
②、RealSubject
真实主题角色,也叫被代理角色,是业务逻辑的具体执行者。
③、Proxy
代理主题角色,也叫代理类,它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并在真实主题角色处理前后做一些预处理或善后工作。
通用代码如下:
/**
* 抽象主题类
*/
public interface Subject {
void doSomething();
}
/**
* 真实主题角色
*/
public class RealSubject implements Subject{
@Override
public void doSomething() {
//TODO 具体执行的事
}
}
/**
* 代理主题角色
*/
public class Proxy implements Subject{
//要代理的具体实现类
private Subject realSubject;
public Proxy(Subject realSubject){
this.realSubject = realSubject;
}
@Override
public void doSomething() {
this.before();
realSubject.doSomething();
this.after();
}
// 预处理
private void before(){
// TODO
}
// 善后处理
private void after(){
// TODO
}
}
3、代理模式的两种实现
比如用代理模式实现统计某个接口的耗时。
3.1 静态代理
①、基于接口编程
抽象主题类:
public interface IUserController {
// 登录
String login(String username,String password);
// 注册
String register(String username,String password);
}
具体主题类:
public class UserController implements IUserController{
@Override
public String login(String username, String password) {
// TODO 登录逻辑
return null;
}
@Override
public String register(String username, String password) {
// TODO 注册逻辑
return null;
}
}
代理主题类:
public class UserControllerProxy implements IUserController{
private IUserController userController;
public UserControllerProxy(IUserController userController){
this.userController = userController;
}
@Override
public String login(String username, String password) {
long startTime = System.currentTimeMillis();
// 登录逻辑
userController.login("username","password");
long endTime = System.currentTimeMillis();
long responseTime = endTime - startTime;
System.out.println("接口响应时间:"+responseTime);
return null;
}
@Override
public String register(String username, String password) {
long startTime = System.currentTimeMillis();
// 注册逻辑
userController.register("username","password");
long endTime = System.currentTimeMillis();
long responseTime = endTime - startTime;
System.out.println("接口响应时间:"+responseTime);
return null;
}
}
测试:
因为原始类 UserController 和代理类 UserControllerProxy 实现相同的接口,是基于接口而非实现编程,将UserController类对象替换为UserControllerProxy类对象,不需要改动太多代码
public class StaticProxyTest {
public static void main(String[] args) {
IUserController userController = new UserControllerProxy(new UserController());
userController.login("username","password");
userController.register("username","password");
}
}
在上面的代码中,代理类和具体主题类需要实现相同的接口,假如具体主题类没有实现接口,并且不是我们开发维护的(比如来自第三方接口),我们要统计这个第三方接口的耗时,那应该如何实现代理模式呢?
②、基于继承
继承具体主题类,然后扩展其方法即可,直接看代码。
public class UserControllerProxy extends UserController {
@Override
public String login(String username, String password) {
long startTime = System.currentTimeMillis();
// 登录逻辑
super.login("username","password");
long endTime = System.currentTimeMillis();
long responseTime = endTime - startTime;
System.out.println("接口响应时间:"+responseTime);
return null;
}
@Override
public String register(String username, String password) {
long startTime = System.currentTimeMillis();
// 注册逻辑
super.register("username","password");
long endTime = System.currentTimeMillis();
long responseTime = endTime - startTime;
System.out.println("接口响应时间:"+responseTime);
return null;
}
}
3.2 动态代理
在上面的例子中,有两个问题:
①、我们需要在代理类中,将具体主题类中的所有的方法,都重新实现一遍,并且为每个方法都附加相似的代码逻辑,如果方法很多,重复代码也会很多。
②、如果要添加的附加功能的类有不止一个,我们需要针对每个类都创建一个代理类。
那该如何解决上面的问题呢?答案就是动态代理(Dynamic Proxy)。
动态代理:不事先为每个原始类编写代理类,而是在运行的时候,动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
JDK动态代理:
public class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = method.invoke(this.target, args);
long endTime = System.currentTimeMillis();
long responseTime = endTime - startTime;
System.out.println("接口响应时间:"+responseTime);
return result;
}
}
测试:
public class DynamicProxyTest {
public static void main(String[] args) {
// 1、创建具体主题类
IUserController userController = new UserController();
// 2、创建 Handler
DynamicProxyHandler proxyHandler = new DynamicProxyHandler(userController);
// 3、动态产生代理类
IUserController o = (IUserController)Proxy.newProxyInstance(userController.getClass().getClassLoader(),
userController.getClass().getInterfaces(),
proxyHandler);
o.login("username","password");
o.register("username","password");
}
}
这是 JDK 动态代理,利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,代理对象是在程序运行时产生的,而不是编译期,要求是具体主题类必须实现接口。
另外一种方式是 Cglib 动态代理。CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,也就是通过修改字节码生成子类来处理。
Cglib 动态代理:
public class UserController{
public String login(String username, String password) {
// TODO 登录逻辑
System.out.println("登录");
return null;
}
public String register(String username, String password) {
// TODO 注册逻辑
System.out.println("注册");
return null;
}
}
注意:真实主题类是没有实现接口的。
public class CglibDynamicProxy implements MethodInterceptor {
private Object target;
public CglibDynamicProxy(Object target){
this.target = target;
}
// 给目标创建代理对象
public Object newProxyInstance(){
// 1.工具类
Enhancer enhancer = new Enhancer();
// 2.设置父类
enhancer.setSuperclass(target.getClass());
// 3.设置回调函数
enhancer.setCallback(this);
// 4.创建子类(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = method.invoke(this.target, args);
long endTime = System.currentTimeMillis();
long responseTime = endTime - startTime;
System.out.println("接口响应时间:"+responseTime);
return result;
}
}
测试:
public class CglibDynamicProxyTest {
public static void main(String[] args) {
UserController userController = new UserController();
CglibDynamicProxy cglibDynamicProxy = new CglibDynamicProxy(userController);
UserController o = (UserController)cglibDynamicProxy.newProxyInstance();
o.login("username","password");
o.register("username","password");
}
}
4、代理模式优点
①、职责清晰
真实的角色就是实现实际的业务逻辑, 不用关心其他非本职责的事务, 通过后期的代理完成一件事务, 附带的结果就是编程简洁清晰。
②、高扩展性
具体主题角色是随时都会发生变化的, 只要它实现了接口, 甭管它如何变化,代理类完全都可以在不做任何修改的情况下使用。
5、代理模式应用场景
①、业务系统的非功能性需求开发
这是最常用的一个场景。比如:监控、统计、鉴权、限流、事务、幂等、日志。我们将这些附加功能与业务功能解耦,放到代理类中统一处理,让程序员只需要关注业务方面的开发。
典型例子就是 SpringAOP。
②、RPC
RPC(远程代理) 框架也可以看作一种代理模式,通过远程代理,将网络通信、数据编解码等细节隐藏起来。客户端在使用 RPC 服务的时候,就像使用本地函数一样,无需了解跟服务器交互的细节。除此之外,RPC 服务的开发者也只需要开发业务逻辑,就像开发本地使用的函数一样,不需要关注跟客户端的交互细节。
Java设计模式之(五)——代理模式的更多相关文章
- java设计模式之Proxy(代理模式)
java设计模式之Proxy(代理模式) 2008-03-25 20:30 227人阅读 评论(0) 收藏 举报 设计模式javaauthorizationpermissionsstringclass ...
- Java设计模式之《代理模式》及应用场景
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6525527.html 代理模式算是我接触较早的模式,代理就是中介,中间人.法律上也有代理, ...
- 重学 Java 设计模式:实战代理模式「模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景」
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 难以跨越的瓶颈期,把你拿捏滴死死的! 编程开发学习过程中遇到的瓶颈期,往往是由于看不 ...
- Java设计模式学习记录-代理模式
代理模式 代理模式是常见设计模式的一种,代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起 ...
- Java设计模式:Proxy(代理)模式
概念定义 代理模式是一种使用代理对象来执行目标对象的方法并在代理对象中增强目标对象方法的一种设计模式. 使用代理模式的原因有: 中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象, ...
- java设计模式-----11、代理模式
Proxy模式又叫做代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理(Proxy)以控制对这个对象的访问. 所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代 ...
- 《Java设计模式》之代理模式 -Java动态代理(InvocationHandler) -简单实现
如题 代理模式是对象的结构模式.代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用. 代理模式可细分为如下, 本文不做多余解释 远程代理 虚拟代理 缓冲代理 保护代理 借鉴文章 ht ...
- Java设计模式—Proxy动态代理模式
代理:设计模式 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 图 1. 代 ...
- Java设计模式9:代理模式
代理模式 代理模式的定义很简单:给某一对象提供一个代理对象,并由代理对象控制对原对象的引用. 代理模式的结构 有些情况下,一个客户不想活着不能够直接引用一个对象,可以通过代理对象在客户端和目标对象之间 ...
- 阿里架构师浅析Java设计模式之虚拟代理模式
虚拟代理模式(Virtual Proxy)是一种节省内存的技术,它建议创建那些占用大量内存或处理复杂的对象时,把创建这类对象推迟到使用它的时候.在特定的应用中,不同部分的功能由不同的对象组成,应用启动 ...
随机推荐
- MacOS下Java与JDK关系与相关路径
MacOS下Java与JDK关系与相关路径 macOS下的Java与JDK的路径曾经困扰过我一段时间,今天稍有些忘记,故记下笔记,整理一下.Java与JDK的关系不在本文笔记之内,Javaer常识. ...
- [AGC023D] Go Home 题解
题目传送门 Solution 首先排除掉特殊情况:若 \(S\) 在两侧,肯定会顺序/逆序直接走完,答案就是边界减去出发点. 考虑到若 \(P_1\geq P_n\),那么显然 \(1\) 不到家 \ ...
- 容器化之路Docker网络核心知识小结,理清楚了吗?
Docker网络是容器化中最难理解的一点也是整个容器化中最容易出问题又难以排查的地方,加上使用Kubernets后大部分人即使是专业运维如果没有扎实的网络知识也很难定位容器网络问题,因此这里就容器网络 ...
- 开源协同OA办公平台教程:O2OA服务管理中,接口的调用权限
本文介绍O2OA服务管理中,接口的权限设定和调用方式. 适用版本:5.4及以上版本 创建接口 具有服务管理设计权限的用户(具有ServiceManager角色或Manager角色)打开" ...
- python 类中的公有属性 私有属性 实例属性
class parent(): i=1 __j=2 class child(parent): m=3 __n=4 def __init__(self,age,name): self.age=age s ...
- SingnalR 从开发到生产部署闭坑指南
前天倒腾了一份[SignalR在react/go技术栈的实践], 步骤和思路大部分是外围框架的应用, 今天趁热打铁, 给一个我总结的SignalR避坑指南. 1.SignalR 默认协商 不管是.NE ...
- 深度剖析Redis6的持久化机制(大量图片说明,简洁易懂)
Redis的强劲性能很大程度上是由于它所有的数据都存储在内存中,当然如果redis重启或者服务器故障导致redis重启,所有存储在内存中的数据就会丢失.但是在某些情况下,我们希望Redis在重启后能够 ...
- Java:HashTable类小记
Java:HashTable类小记 对 Java 中的 HashTable类,做一个微不足道的小小小小记 概述 public class Hashtable<K,V> extends Di ...
- 第六次Scrum Metting
日期:2021年5月3日 会议主要内容概述:讨论前后端进度,前端各模块对接以及前后端对接. 一.进度情况 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 徐宇龙 后端 数据模块 ...
- “妈妈再也不用担心我忘交作业了!”——记2020BUAA软工团队项目选择
写在前面 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任建) 这个作业的要求在哪里 团队项目选择 项目简介 项目名称:北航学生资源整合和作业提醒平台 项目内容: 设计实现一 ...