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

news2024/11/17 4:25:58

文章目录

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

代理模式

定义

代理模式,为其他对象提供一种代理以控制对这个对象的访问
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.coloradmin.cn/o/636789.html

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

相关文章

什么是Vue的JSX语法?如何使用JSX语法?

什么是Vue的JSX语法?如何使用JSX语法? 在Vue中,我们通常使用模板语法来编写组件的模板。但是,有些开发者更喜欢使用类似于React的JSX语法来编写组件。Vue也支持使用JSX语法来编写组件,本文将介绍什么是Vue的JSX语法以…

企业级信息系统开发讲课笔记4.7 Spring Boot整合JPA

文章目录 零、学习目标一、Spring Data JPA概述1、Spring Data JPA简介2、Spring Data JPA基本使用3、使用Spring Data JPA进行数据操作的多种实现方式4、自定义Repository接口中的Transactional注解5、变更操作,要配合使用Query与Modify注解 二、Spring Boot整合JP…

热门图表软件推荐,哪款更功能更强大?

在如今的数据化时代,各种企业都需要有一套高效的报表制作工具。而图表是报表中最常用、也是最重要的一部分,因此选择一款优秀的图表软件显得尤为重要。本文将为大家介绍5款热门图表软件,并突出介绍VeryReport图表软件的优势。 1. VeryReport…

NetApp 全闪存 ASA 系统可为您的任务关键型企业级应用程序、数据库和 VMware 基础架构提供简单专用的块存储

NetApp ASA:全闪存 SAN 阵列 在性能和效率之间进行艰难抉择的时代已经过去。NetApp ASA 系统提供简单专用的块存储,具有卓越的性能、高可用性和领先的效率 — 无需权衡取舍。 为什么选择适用于 SAN 的 NetApp ASA 系统? 简单的 SAN 存储&…

DCL单例及synchrosized问题

疑问待解: 1 synchronized代码块执行完后,在没有return INSTANCE之前,其他线程是否可见这个对象(因为synchronized出块后会把工作内存写到主存)? 如果可见,那么return的作用是不是可有可无&…

object类型(equals、hashCode、getClass、getName)

equals方法的改写 Override//重写equals方法,重写方法后对比的属性值(没有重写前对比的是属性值)public boolean equals(Object obj) {Students s (Students) obj;return this.name.equals(s.name) && this.age s.age;}public clas…

学顶教育:中级统计师单科成绩计算方式分享!

中级统计师的成绩管理是非滚动式的,所以需要考生一次考下两个科目,两个科目都合格之后,才能取得证书。 中级统计师的考试,是每年举行一次,全部考试科目合格者,授予由人事部统一印制、全国范围内有效的《统…

从结构上浅谈FPGA LCMXO2-4000HC-6BG256I 实现逻辑的基本原理

LCMXO2-4000HC-6BG256I lattice莱迪思深力科 MachXO2 可编程逻辑器件 (PLD) 由六个超低功耗、即时启动、非易失性 PLD 组成,可提供 256 至 6864 个查找表 (LUT) 的密度。 MachXO2 系列 PLD 提供多种特性,例如嵌入式块 RAM (EBR)、分布式 RAM 和用户闪存 …

叽里呱啦 Nacos 1.1.4 升级 1.4.1 最佳实践

博主介绍:✌全网粉丝4W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战、定制、远程,博主也曾写过优秀论文,查重率极低,在这方面…

VTK学习之读取图片,vtkImageViewer2的使用

一、vtk中的vtkImageData VTK提供相对应的类对图像文件进行读写操作 测试下效果&#xff1a; int main() {//vtkSmartPointer<vtkBMPReader> reader vtkSmartPointer<vtkBMPReader>::New();//reader->SetFileName("**\\12.bmp");vtkSmartPointer&l…

【分享】Heic图片如何批量转换成jpg格式?

我们知道&#xff0c;Heic是苹果产品的专属图片格式&#xff0c;但不是所有Windows系统都可以查看&#xff0c;而且很多需要上传图片的平台也不支持Heic格式&#xff0c;这些情况就需要把Heic转换成JPG等常用的图片格式。 如果图片数量非常多&#xff0c;要如何实现批量转换呢…

<Windows>《Windows当前桌面壁纸的位置》

《Windows当前桌面壁纸的位置》 问题&#xff1a; 想找到当前桌面壁纸的位置&#xff0c;该怎么做&#xff1f; 解决&#xff1a; win7之后&#xff0c;windows壁纸以注册表存在。注册表位置为&#xff1a; HKEY_CURRENT_USER\Control Panel\Desktop\Wallpaper该值记录了位置…

JWT验证

JSON Web Token 入门教程 - 阮一峰的网络日志 (ruanyifeng.com) 补充上时间线&#xff1f;画图&#xff1f; 隐患是什么 为什么一开始不这么做 这个封面挺好做的&#xff0c;以后笔记我也做一个&#xff0c;&#xff0c;要是能自动生成就好了 一、认证 为了保存信息用的&#…

【深度学习】日常笔记3

如果分类问题具有预测这样带有自然顺序的问题&#xff0c;如{婴⼉, ⼉童, ⻘少年, ⻘年⼈, 中年⼈, ⽼年⼈}&#xff0c;那么可以把分类问题转变为回归问题了。不过可以使用独热编码one-hot encoding。 类别对应的分量设置为1&#xff0c;其他所有分量设置为0。在我们的例⼦中…

Cadence原理图快速查找元器件的方法

1.Cadence原理图快速查找元器件的方法 ①在红框中输入元器件编号&#xff0c;点击望远镜的图标在底下的状态栏可看到查找到的相关元器件&#xff0c;点击元器件可自动定位当前元器件的位置。 ②点击hierarchy&#xff08;层&#xff09;可自主查找&#xff0c;找到后点击序号即…

【项目实战】一、Spring boot整合JWT、Vue案例展示用户鉴权

前言 案例整合了Spring boot、Spring Cloud alibaba、Gateway、Nacos discovery、Nacos config、openFeign、JWT、Vue3、Router、Axios等&#xff1b;通过JWT和登录、查询&#xff08;带用户信息&#xff09;接口&#xff0c;验证了上述工具以及鉴权功能。 1、若无公共模块&a…

学好Java爬虫需要什么技巧

Java爬虫是一种利用Java编程语言编写的网络爬虫程序&#xff0c;它可以自动化地浏览和抓取互联网上的数据&#xff0c;并将数据进行处理和保存。Java爬虫通常使用HTTP协议模拟浏览器请求来获取网页内容&#xff0c;并通过解析HTML网页标签和属性等信息来提取有用的数据。Java爬…

PPT处理控件Aspose.Slides入门教程:在 C# 中加密和解密 PPT

Aspose API支持流行文件格式处理&#xff0c;控件覆盖 word、excel、PDF、条码、OCR、CAD、HTML、email、ppt、等各个文档管理领域 是一款 PowerPoint管理API&#xff0c;用于读取&#xff0c;编写&#xff0c;操作和转换PowerPoint幻灯片的独立API&#xff0c;可将PowerPoint…

【网页设计】第 2 课 - 网页设计规范

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、网页规范 3、设计规范 4、banner 简介 4.1、Banner 的定义 4.2、Banner 的类型 4.3、Banner 构图 4.4、…

chatgpt赋能python:Python学习笔记:如何合并元组

Python学习笔记&#xff1a;如何合并元组 在Python中&#xff0c;元组是一种不可变的数据结构。当我们需要组合不同的元组时&#xff0c;我们可以使用元组合并的方法来实现。在本文中&#xff0c;我们将学习如何使用Python语言来合并元组。 什么是元组 在Python语言中&#…