Java动态代理:优化静态代理模式的灵活解决方案

news/2024/11/30 5:39:12/

文章目录

  • 代理模式
    • 定义
    • 具体实现
    • 分析优缺点
  • 优化使用动态代理解决
    • 优化
    • 相关知识
      • 动态代理种类
      • 场景应用

代理模式

定义

代理模式,为其他对象提供一种代理以控制对这个对象的访问
image.png

具体实现

代理模式的具体实现描述可以分为以下几个步骤:

  1. 创建抽象对象接口(Subject Interface):
    • 定义抽象对象接口,包含原始对象和代理对象共同实现的方法。
/*** 抽象对象接口**/
public interface UserManager {public void addUser(String userId, String userName);public void delUser(String userId);public void modifyUser(String userId, String userName);public String findUser(String userId);
}

image.png
2. 创建原始对象(Real Object):

  • 实现抽象对象接口的具体类,即原始对象。
public class UserManagerImpl implements UserManager {public void addUser(String userId, String userName) {try {System.out.println("UserManagerImpl.addUser() userId-->>" + userId);}catch(Exception e) {e.printStackTrace();throw new RuntimeException();}	}public void delUser(String userId) {System.out.println("UserManagerImpl.delUser() userId-->>" + userId);}public String findUser(String userId) {System.out.println("UserManagerImpl.findUser() userId-->>" + userId);return "张三";}public void modifyUser(String userId, String userName) {System.out.println("UserManagerImpl.modifyUser() userId-->>" + userId);}}

image.png
3. 创建代理对象(Proxy Object):

  • 实现抽象对象接口的具体类,即代理对象。
  • 在代理对象中持有一个对原始对象的引用。
/*** 代理类**/
public class UserManagerImplProxy implements UserManager {private UserManager userManager;public UserManagerImplProxy(UserManager userManager) {this.userManager = userManager;}public void addUser(String userId, String userName) {try {System.out.println("start-->>addUser() userId--代理类-->>" + userId);userManager.addUser(userId, userName);System.out.println("success--代理类-->>addUser()");}catch(Exception e) {e.printStackTrace();System.out.println("error--代理类-->>addUser()");}	}public void delUser(String userId) {}public String findUser(String userId) {return null;}public void modifyUser(String userId, String userName) {}}
  1. 在代理对象中添加额外逻辑:
    • 在代理对象的方法中,可以在调用原始对象之前或之后执行一些额外的逻辑。
    • 这些额外逻辑可以是权限验证、日志记录、缓存操作等。
      image.png
  2. 在客户端中使用代理对象:
    • 在客户端代码中,通过代理对象来访问原始对象的方法。
    • 客户端不直接访问原始对象,而是通过代理对象进行间接访问。
public class Client {/*** @param args*/public static void main(String[] args) {//创建代理对象,同时将被代理的对象通过构造函数传入到代理对象中UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());//调用代理对象的方法userManager.addUser("0001", "张三");}}

image.png

分析优缺点

上面写的代码都是静态代理模式的,所以这里罗列优缺点的基础也是基于静态代理的:
优点:

  1. 额外功能:静态代理可以在代理对象中添加额外的功能,而无需修改真实对象的代码。这使得我们可以在不影响真实对象的情况下,对其进行功能扩展,例如添加日志记录、性能监控等。

  2. 控制访问:静态代理可以在代理对象中控制对真实对象的访问。代理对象可以验证参数、权限等,并决定是否允许客户端访问真实对象。

  3. 解耦合:静态代理可以将真实对象与客户端代码解耦合。客户端只需要通过代理对象与真实对象进行交互,而无需关注真实对象的具体实现。

缺点:

  1. 代码重复:静态代理需要手动创建代理对象,并在代理对象中实现与真实对象相同的接口。这可能导致代理对象和真实对象之间的代码重复,增加了维护的工作量。

  2. 增加类的数量:静态代理需要为每个真实对象创建一个代理对象,这可能导致类的数量增加。如果需要代理多个真实对象,就需要为每个真实对象创建一个代理对象,这可能导致类的爆炸增长。

  3. 编译时确定:静态代理在编译时就确定了代理对象和真实对象的关系,无法动态改变。如果需要在运行时动态决定代理对象的行为,静态代理就无法满足需求。

优化使用动态代理解决

优化

使用动态代理来优化上述的静态代理代码,可以解决静态代理中的一些不足之处,如代码重复和类数量增加。下面是使用动态代理进行优化的示例代码:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class DynamicProxyDemo {public static void main(String[] args) {UserManager userManager = new UserManagerImpl();UserManager proxy = (UserManager) getProxyInstance(userManager);proxy.addUser("0001", "张三");}public static Object getProxyInstance(Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new DynamicProxyHandler(target));}
}class DynamicProxyHandler implements InvocationHandler {private Object target;public DynamicProxyHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result;try {System.out.println("start-->>" + method.getName() + "() userId--代理类-->>" + args[0]);result = method.invoke(target, args);System.out.println("success--代理类-->>" + method.getName() + "()");} catch (Exception e) {e.printStackTrace();System.out.println("error--代理类-->>" + method.getName() + "()");throw new RuntimeException();}return result;}
}

通过使用动态代理,我们可以避免手动创建代理对象和实现代理接口的繁琐工作。在上述代码中,我们创建了一个DynamicProxyHandler类实现了InvocationHandler接口,它负责处理代理对象的方法调用。在getProxyInstance方法中,我们使用Proxy.newProxyInstance动态生成代理对象,并将DynamicProxyHandler作为参数传入。最后,我们可以直接调用代理对象的方法,实现对真实对象的代理。

使用动态代理优化静态代理的不足之处包括:

  1. 代码重复问题:使用动态代理,我们不需要为每个真实对象编写一个单独的代理类,而是可以在运行时动态生成代理对象。这样可以减少重复的代理类代码。

  2. 类数量增加问题:静态代理需要为每个真实对象创建一个对应的代理类,从而导致类的数量增加。而使用动态代理,我们只需要一个通用的代理类,可以代理多个真实对象。

  3. 动态性:动态代理在运行时动态生成代理对象,可以灵活地控制代理对象的行为。相比之下,静态代理在编译时就确定了代理对象和真实对象的关系,无法在运行时动态改变。

相关知识

动态代理种类

在Java中,有两种常见的动态代理方式:基于接口的动态代理和基于类的动态代理。

  1. 基于接口的动态代理(Interface-based Dynamic Proxy):
    这种动态代理方式是基于接口进行代理的,使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。首先定义一个接口,然后创建一个实现InvocationHandler接口的代理处理器类,在处理器类中实现代理对象方法的增强逻辑。使用Proxy.newProxyInstance方法创建代理对象,传入类加载器、接口数组和代理处理器对象。代理对象可以调用接口中定义的方法,并在调用前后执行代理处理器中的逻辑。

  2. 基于类的动态代理(Class-based Dynamic Proxy):
    基于类的动态代理是使用第三方库,如CGLib,来实现的。CGLib是一个强大的字节码增强库,它可以在运行时生成代理类,无需接口。通过继承目标类并重写其方法,CGLib能够在方法调用前后注入增强逻辑。使用CGLib的Enhancer类可以创建代理对象,并设置代理的父类、回调对象(实现MethodInterceptor接口),以及其他属性。代理对象可以调用父类中的方法,并在调用前后执行回调对象中的逻辑。

在上面的优化代码,使用的是JDK提供的动态代理实现方式,这种实现方式是基于接口的。这种基于接口方式的在uml类图上更加符合我们上面提供的类图结构。而cglib的代理方式是继承被代理类的方式,作为子类里式替换进行代理的。

场景应用

动态代理模式在开发中有许多场景应用,下面列举了几个常见的应用场景:

  1. 日志记录:通过使用动态代理,可以在方法调用前后记录日志信息,包括方法名、参数、执行时间等。这样可以方便地进行系统日志记录和调试,提高代码的可维护性和可调试性。

  2. 权限控制:动态代理可以在方法调用前进行权限验证,判断用户是否有权限执行该方法。这样可以实现细粒度的权限控制,保护系统中重要的功能不被非授权用户访问。

  3. 缓存操作:动态代理可以在方法调用前后进行缓存操作,例如查询数据时先检查缓存中是否存在该数据,如果存在则直接返回缓存数据,否则再查询数据库并将结果存入缓存中。这样可以提高系统的访问速度和性能。

  4. 事务管理:通过使用动态代理,可以在方法调用前后进行事务管理,包括开启事务、提交事务或回滚事务等操作。这样可以保证数据库操作的一致性和可靠性,防止数据丢失或不一致的情况发生。比如在spring框架下的@Transactional注解就是通过动态代理的方式进行管理事务的

  5. 性能监控:动态代理可以在方法调用前后进行性能监控,统计方法的执行时间和调用次数等信息。这样可以对系统的性能进行监控和优化,发现潜在的性能问题并进行针对性的优化措施。

  6. 远程调用:动态代理可以在方法调用时将请求发送到远程服务器,并接收远程服务器的响应结果。这样可以实现分布式系统之间的通信和协作,提供远程服务调用的能力。

总的来说,动态代理模式在开发中的应用非常广泛,它可以通过在方法调用前后注入逻辑来实现额外的功能,而不需要修改原始对象的代码。这种灵活性使得动态代理成为解决许多横切关注点(cross-cutting concerns)的有效方式,如日志记录、权限控制、缓存操作、事务管理等。


http://www.ppmy.cn/news/343186.html

相关文章

删除的excel表格怎么恢复

Excel是许多人工作中必不可少的软件,它可以用来进行数据分析、报告制作和财务管理等工作。但在使用Excel时,偶尔会出现一些误操作,例如不小心删除了一个重要的Excel表格。那么删除的excel表格怎么恢复呢?本文将为大家介绍几种恢复删除Excel表…

BUUCTF Alice与Bob 1

题目描述: 密码学历史中,有两位知名的杰出人物,Alice和Bob。他们的爱情经过置换和轮加密也难以混淆,即使是没有身份认证也可以知根知底。就像在数学王国中的素数一样,孤傲又热情。下面是一个大整数:98554799767,请分解…

【JavaWeb】IDEA专业版和社区版创建Servlet项目

文章目录 1. 什么是Servlet2. 创建项目3. 引入依赖3.1 在pom.xml中引入依赖3.2 下载jar包引入依赖 4. 创建目录5. 编写代码验证6.总结 1. 什么是Servlet Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app.&#xf…

盛元广通生物化学重点实验室化学品信息化安全管理系统

生物化学重点实验室是国家基础研究和高技术研究的重要基地,是培养和造就高层次创新型人才的重要基地。为保障实验室化学品安全使用,实验人员可通过现场或移动端管理系统实现化学品安全使用与存储。盛元广通生物化学重点实验室化学品信息化安全管理系统具…

洗地机和吸尘器哪个实用,洗地机优点和缺点

很多人都有一个疑问,洗地机跟吸尘器到底有什么区别,其实十分容易理解,吸尘器只能处理干垃圾,对于液态垃圾、固态垃圾都难以清洁,所以在日常适用当中,吸尘器可以支持使用场景有限,洗地机能够清除…

家用洗地机哪种好用?洗地机分享

洗地机边吸尘边拖洗,干湿垃圾一步搞定,一定程度上满足了很多家庭的日常清洁需求。作为重新定义家庭清洁生活的便利产品,不仅大大减轻了日常清洁的工作量,深受年轻消费群体喜爱,被不少消费者列入购物清单。大家在购买洗…

华为OD机试真题 JavaScript 实现【记票统计】【牛客练习题】

一、题目描述 请实现一个计票统计系统。你会收到很多投票,其中有合法的也有不合法的,请统计每个候选人得票的数量以及不合法的票数。 (注:不合法的投票指的是投票的名字不存在n个候选人的名字中!!&#x…

删除链表的倒数第N个节点

目录 题目描述 运行代码 执行示例 最终提交 题目描述 运行代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* Li…