Java代理模式探究
创始人
2025-05-31 15:13:35
0

代理模式

我们使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

举个例子来说就是相当于我们卖房子,需要找中介,中介就是代理对象!

代理模式主要解决了在不改变原有对象的基础上,可以扩展原有对象的功能!

代理模式主要分为静态代理和动态代理。

1、静态代理

相当于我们要对目标对象中的每个方法都要进行增强,步骤繁琐,不推荐静态代理,只需要掌握他是一个啥样的过程即可!!!

(1)客户接口:

package com.day20230320;public interface Customer {void sell();
}

(2)客户接口实现类:

package com.day20230320;/*** @author * @version 1.0* @description TODO* @date 2023/3/20 15:14*/public class CustomerImpl implements Customer{@Overridepublic void sell() {System.out.println("我是客户我要买房子");}
}

(3)中介对象,将客户信息保存到中介的通讯录中。

package com.day20230320;/*** @author * @version 1.0* @description TODO* @date 2023/3/20 15:15*/public class ProxyCustomer {private Customer customer;public ProxyCustomer(Customer customer){this.customer = customer;}public void sell(){System.out.println("我是中介,我要开始为客户买房子了");customer.sell();}
}

(4)测试:

package com.day20230320;/*** @author * @version 1.0* @description TODO* @date 2023/3/20 15:18*/public class ProxyMainDemo {public static void main(String[] args) {CustomerImpl customer = new CustomerImpl();ProxyCustomer pc = new ProxyCustomer(customer);pc.sell();}
}

在这里插入图片描述

2、动态代理

相比较静态代理而言,动态代理更加灵活,不用挨个去实现接口中方法,也不用必须实现接口。并且动态代理更是一种思想,这里主要以面试经常提起的JDK动态代理和CGLIB动态代理为例。从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

JDK动态代理机制

CGLIB动态代理机制

JDK动态代理

JDK动态代理机制中,核心是InvocationHandler 接口和 Proxy 类。

通过Proxy类的newProxyInstance()方法来创建对象,而通过实现InvocationHandler 接口类中的invoke方法处理逻辑。

当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

简单描述:

通过Proxy 类的 newProxyInstance() 创建的代理对象,在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。

还是以顾客买房为例:

(1)通过实现InvocationHandler 接口类的 invoke 方法,来处理逻辑!

package com.day20230320.JdkProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** @author * @version 1.0* @description TODO* @date 2023/3/20 15:41*/public class JdkProxy implements InvocationHandler {private Object object;public JdkProxy(Object object){this.object = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是中介,我要开始买房子了" + method.getName());Object result = method.invoke(object, args);System.out.println("我是中介,房子已经卖完了"+method.getName());return result;}
}

(2)通过Proxy类的newProxyInstance()方法来创建代理对象!

package com.day20230320.JdkProxy;import java.lang.reflect.Proxy;/*** @author * @version 1.0* @description TODO* @date 2023/3/20 15:48*/public class JdkProxyFactory {public static Object getProxy(Object target){return Proxy.newProxyInstance(target.getClass().getClassLoader(),//目标类的类加载target.getClass().getInterfaces(),//代理需要实现的接口,可指定多个new JdkProxy(target)//代理对象的自定义);}
}

(3)测试

package com.day20230320.JdkProxy;/*** @author * @version 1.0* @description TODO* @date 2023/3/20 15:51*/public class MainTest {public static void main(String[] args) {Customer proxy = (Customer) JdkProxyFactory.getProxy(new CustomerImpl());proxy.sell();System.out.println("-------");proxy.notSell();}
}

在这里插入图片描述

JDK动态代理机制只能直接代理接口或者代理实现了接口的类,但是对于为实现任何接口的类是无法代理的,针对于这种情况,CGLIB 可以代理未实现任何接口的类。

CGLIB 动态代理机制

在 CGLIB 动态代理机制中 MethodInterceptor 接口和 Enhancer 类是核心。

(1)你需要自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法。

(2)你可以通过 Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法。

注意的是CGLIB 是一个开源的项目,需要手动添加相关依赖。

cglibcglib3.3.0

自定义 MethodInterceptor 并重写 intercept 方法

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** 自定义MethodInterceptor*/
public class DebugMethodInterceptor implements MethodInterceptor {/*** @param o           被代理的对象(需要增强的对象)* @param method      被拦截的方法(需要增强的方法)* @param args        方法入参* @param methodProxy 用于调用原始方法*/@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//调用方法之前,我们可以添加自己的操作System.out.println("before method " + method.getName());Object object = methodProxy.invokeSuper(o, args);//调用方法之后,我们同样可以添加自己的操作System.out.println("after method " + method.getName());return object;}}

通过 Enhancer类来动态获取被代理类

import net.sf.cglib.proxy.Enhancer;public class CglibProxyFactory {public static Object getProxy(Class clazz) {// 创建动态代理增强类Enhancer enhancer = new Enhancer();// 设置类加载器enhancer.setClassLoader(clazz.getClassLoader());// 设置被代理类enhancer.setSuperclass(clazz);// 设置方法拦截器enhancer.setCallback(new DebugMethodInterceptor());// 创建代理类return enhancer.create();}
}

3、JDK 动态代理和 CGLIB 动态代理对比

(1)JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
(2)就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

4、静态代理和动态代理的对比

(1)灵活性 :动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
(2)JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

5、应用

(1)Mybatis的Dao层接口是如何与xml文件进行关联的?

Dao 接口的工作原理是 JDK 动态代理,MyBatis 运行时会使用 JDK 动态代理为 Dao 接口生成代理 proxy 对象,代理对象 proxy 会拦截接口方法,转而执行 MappedStatement 所代表的 sql,然后将 sql 执行结果返回。

(2)Spring AOP、RPC 框架应该是两个不得不提的,它们的实现都依赖了动态代理。

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...