Java的动态代理(实际案例秒懂!)

2024-09-01 17:20
文章标签 java 动态 代理 案例 实际

本文主要是介绍Java的动态代理(实际案例秒懂!),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在看动态代理解决两个案例之前,请先看链接VCR

《java代理》2分钟动画_哔哩哔哩_bilibili

一.动态代理-精致小案例

需求分析

传统方法

就是定义一个接口,然后实现类去实现规定的run方法

缺点:代码很冗余,有一些运行前和运行后的代码都给了实现类,难以管理

 

 动态代理方法

使用动态代理,利用反射机制,根据方法决定调用哪个对象的方法

添加VehicleProxyProvider类

package me;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @version 1.0* VehicleProxyProvider 该类可以返回一个代理对象.*/
public class VehicleProxyProvider {//定义一个属性//target_vehicle 表示真正要执行的对象//该对象实现了Vehicle接口private Vehicle target_vehicle;//构造器public VehicleProxyProvider(Vehicle target_vehicle) {this.target_vehicle = target_vehicle;}//编写一个方法,可以返回一个代理对象, 该代理对象可以通过反射机制调用到被代理对象的方法public Vehicle getProxy() {//得到类加载器ClassLoader classLoader =target_vehicle.getClass().getClassLoader();//得到要代理的对象/被执行对象 的接口信息,底层是通过接口来完成调用Class<?>[] interfaces = target_vehicle.getClass().getInterfaces();//创建InvocationHandler 对象//因为 InvocationHandler 是接口,所以我们可以通过匿名对象的方式来创建该对象/**** public interface InvocationHandler {*  public Object invoke(Object proxy, Method method, Object[] args)*         throws Throwable;* }* invoke 方法是将来执行我们的target_vehicle的方法时,会调用到**/InvocationHandler invocationHandler = new InvocationHandler() {/*** invoke 方法是将来执行我们的target_vehicle的方法时,会调用到* @param o 表示代理对象* @param method 就是通过代理对象调用方法时,的哪个方法 代理对象.run()* @param args : 表示调用 代理对象.run(xx) 传入的参数* @return 表示 代理对象.run(xx) 执行后的结果.* @throws Throwable*/@Overridepublic Object invoke(Object o, Method method, Object[] args)throws Throwable {System.out.println("交通工具开始运行了....");//这里是我们的反射基础 => OOP//method 是?: public abstract void com.hspedu.spring.proxy2.Vehicle.run()//target_vehicle 是? Ship对象//args 是null//这里通过反射+动态绑定机制,就会执行到被代理对象的方法//执行完毕就返回Object result = method.invoke(target_vehicle, args);System.out.println("交通工具停止运行了....");return result;}};/*public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)老师解读1. Proxy.newProxyInstance() 可以返回一个代理对象2. ClassLoader loader: 类的加载器.3. Class<?>[] interfaces 就是将来要代理的对象的接口信息4. InvocationHandler h 调用处理器/对象 有一个非常重要的方法invoke*/Vehicle proxy =(Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}

最重要的代码就是 :

Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

这是提供最后代理对象实例的方法:参数有三:

        @你要求代理对象的类加载器

        @你要求代理对象的接口信息

        @以及调用处理器

底层是用反射机制来实现的。

测试方法:

public class VehicleProxyTest {@Testpublic void test01(){Vehicle vehicle = new Ship();//传入我们需要代理的对象VehicleProxyProvider vehicleProxyProvider = new VehicleProxyProvider(vehicle);//获取代理对象Vehicle proxy = vehicleProxyProvider.getProxy();//通过代理对象执行对应的run()方法proxy.run();}
}

以及真正的运行结果:
 

 真正解析这个代码

所谓的这个代理,就可以将我们的Vehicle接口看做是一个老板。我们的car类和ship类就是员工。现在老板给这两个员工一个run的工作,但是因为在run工作的前后他们都要输出相同的一句话。这个输出同一句话没必要让每个员工都写一遍,于是就把这个输出同一句话的事情交给了一个代理对象,帮我们输出这句话。代理对象就是老板的助理。

 


这个代码提供代理对象的类:

当你调用proxy.run()方法的时候:会进入到调用器InvocationHandler的invoke方法去

这个调用器中的Object o就是你传入的ship对象,method就是run()方法,arg就是run()里面的参数。

Object result = method.invoke(target_vehicle, args);

方法.invoke(对象)这是反射调用对象ship的run()方法。返回一个结果,

 可以自己去debug一下这个代码。

二.动态代理的深入[横切关注点]

需求分析

 

传统的方法

定义接口:

package proxy;/*** @version 1.0* 接口*/
public interface SmartAnimalable {//求和float getSum(float i, float j);//求差float getSub(float i, float j);
}

 传统的实现子类

package proxy;/*** @version 1.0*/
public class SmartDog implements SmartAnimalable {@Overridepublic float getSum(float i, float j) {//System.out.println("日志-方法名-getSum-参数 " + i + " " + j);float result = i + j;System.out.println("方法内部打印result = " + result);//System.out.println("日志-方法名-getSum-结果result= " + result);return result;}@Overridepublic float getSub(float i, float j) {//System.out.println("日志-方法名-getSub-参数 " + i + " " + j);float result = i - j;System.out.println("方法内部打印result = " + result);//System.out.println("日志-方法名-getSub-结果result= " + result);return result;}
}

结果:

 

动态代理的方法 

MyProxyProvider

package proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;/*** @version 1.0* 可以返回一个动态代理对象, 可以执行SmartDog对象的方法*/
public class MyProxyProvider {//定义我们要执行的目标对象, 该对象需要实现SmartAnimalableprivate SmartAnimalable target_obj;//构造器public MyProxyProvider(SmartAnimalable target_obj) {this.target_obj = target_obj;}//方法, 可以返回代理对象,该代理对象可以执行目标对象public SmartAnimalable getProxy() {//1. 先到的类加载器/对象ClassLoader classLoader = target_obj.getClass().getClassLoader();//2. 得到要执行的目标对象的接口信息Class<?>[] interfaces = target_obj.getClass().getInterfaces();//3. 创建InvocationHandlerInvocationHandler invocationHandler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try {System.out.println("方法执行前-日志-方法名-" + method.getName() + "-参数 "+ Arrays.asList(args)); //这里从AOP看,就是一个横切关注点-前置通知//使用反射调用方法result = method.invoke(target_obj, args);System.out.println("方法执行正常结束-日志-方法名-" + method.getName() + "-结果result= "+ result);//从AOP看, 也是一个横切关注点-返回通知} catch (Exception e) {e.printStackTrace();//如果反射执行方法时,出现异常,就会进入到catch{}System.out.println("方法执行异常-日志-方法名-" + method.getName()+ "-异常类型=" + e.getClass().getName());//从AOP看, 也是一个横切关注点-异常通知} finally {//不管你是否出现异常,最终都会执行到finally{}//从AOP的角度看, 也是一个横切关注点-最终通知System.out.println("方法最终结束-日志-方法名-" + method.getName());}return result;}};//创建代理对象SmartAnimalable proxy =(SmartAnimalable)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return proxy;}
}

 Test方法

  @Testpublic void smartDogTestByProxy() {SmartAnimalable smartAnimalable = new SmartDog();MyProxyProvider myProxyProvider =new MyProxyProvider(smartAnimalable);//我们返回了代理对象SmartAnimalable proxy =myProxyProvider.getProxy();proxy.getSum(10, 2);System.out.println("====================");proxy.getSub(10, 2);}

这篇关于Java的动态代理(实际案例秒懂!)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1127568

相关文章

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

将Java项目提交到云服务器的流程步骤

《将Java项目提交到云服务器的流程步骤》所谓将项目提交到云服务器即将你的项目打成一个jar包然后提交到云服务器即可,因此我们需要准备服务器环境为:Linux+JDK+MariDB(MySQL)+Gi... 目录1. 安装 jdk1.1 查看 jdk 版本1.2 下载 jdk2. 安装 mariadb(my

SpringBoot中配置Redis连接池的完整指南

《SpringBoot中配置Redis连接池的完整指南》这篇文章主要为大家详细介绍了SpringBoot中配置Redis连接池的完整指南,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以... 目录一、添加依赖二、配置 Redis 连接池三、测试 Redis 操作四、完整示例代码(一)pom.

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析