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 框架应该是两个不得不提的,它们的实现都依赖了动态代理。

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...