Java 代理模式
1、代理模式
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。
代理(Proxy)模式结构图如下:
JAVA的代理有如下3种:
- 静态代理
- 动态代理
- cglib代理
2、静态代理
接口类AdminService.java接口
1 package com.lance.proxy.demo.service; 2 3 public interface AdminService { 4 void update(); 5 Object find(); 6 }
实现类AdminServiceImpl.java
1 package com.lance.proxy.demo.service; 2 3 public class AdminServiceImpl implements AdminService{ 4 public void update() { 5 System.out.println("修改数据"); 6 } 7 8 public Object find() { 9 System.out.println("查找数据"); 10 return new Object(); 11 } 12 }
代理类AdminServiceProxy.java
1 public class AdminServiceProxy implements AdminService{ 2 private AdminService adminService; 3 4 public AdminServiceProxy(AdminService adminService) { 5 this.adminService = adminService; 6 } 7 @Override 8 public void update() { 9 System.out.println("查询权限判断"); 10 adminService.update(); 11 System.out.println("更新结束"); 12 } 13 14 @Override 15 public Object find() { 16 System.out.println("判断查找权限"); 17 Object o = adminService.find(); 18 System.out.println("查找结束"); 19 return o; 20 } 21 }
测试类StaticProxyTest.java
1 public class StaticProxyTest { 2 public static void main(String[] args) { 3 AdminService adminService = new AdminServiceImpl(); 4 AdminServiceProxy proxy = new AdminServiceProxy(adminService); 5 proxy.update(); 6 System.out.println("==========================="); 7 proxy.find(); 8 } 9 }
结果如下:
总结:
静态代理模式在不改变目标对象的前提下,实现了对目标对象的功能扩展。
不足:
静态代理实现了目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。
3、JDK动态代理
为解决静态代理对象必须实现接口的所有方法的问题,Java给出了动态代理,动态代理具有如下特点:1、Proxy对象不需要implements接口;2、Proxy对象的生成利用JDK的Api,在JVM内存中动态的构建Proxy对象。需要使用java.lang.reflect.Proxy类的

方法,方法参数说明:
ClassLoader loader:指定当前target对象使用类加载器,获取加载器的方法是固定的;
Class<?>[] interfaces:target对象实现的接口的类型,使用泛型方式确认类型
nvocationHandler invocationHandler:事件处理,执行target对象的方法时,会触发事件处理器的方法,会把当前执行target对象的方法作为参数传入。
AdminServiceImpl.java和AdminService.java和原来一样。
AdminServiceInvocation.java
1 public class AdminServiceInvocation implements InvocationHandler { 2 private Object target; 3 4 public AdminServiceInvocation(Object target){ 5 this.target = target; 6 } 7 8 @Override 9 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 10 System.out.println("判断权限"); 11 Object invoke = method.invoke(target); 12 System.out.println("操作结束"); 13 return invoke; 14 } 15 }
AdminServiceDynamicProxy.java
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Proxy; 3 4 public class AdminServiceDynamicProxy { 5 private Object target; 6 private InvocationHandler invocationHandler; 7 8 public AdminServiceDynamicProxy(Object target,InvocationHandler invocationHandler){ 9 this.target = target; 10 this.invocationHandler = invocationHandler; 11 } 12 13 public Object getPersonProxy() { 14 Object obj = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocationHandler); 15 return obj; 16 } 17 }
DynamicProxyTest.java
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 5 public class DynamicProxyTest { 6 public static void main(String[] args) { 7 8 // 方法一 9 System.out.println("============ 方法一 =============="); 10 AdminService adminService = new AdminServiceImpl(); 11 System.out.println("代理的目标对象:" + adminService.getClass()); 12 AdminServiceInvocation adminServiceInvocation = new AdminServiceInvocation(adminService); 13 AdminService proxy = (AdminService) new AdminServiceDynamicProxy(adminService, adminServiceInvocation).getPersonProxy(); 14 System.out.println("代理对象:" + proxy.getClass()); 15 16 Object obj = proxy.find(); 17 System.out.println("find 返回对象:" + obj.getClass()); 18 System.out.println("----------------------------------"); 19 proxy.update(); 20 21 //方法二 22 System.out.println("============ 方法二 =============="); 23 AdminService target = new AdminServiceImpl(); 24 AdminServiceInvocation invocation = new AdminServiceInvocation(adminService); 25 AdminService proxy2 = (AdminService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), invocation); 26 27 Object obj2 = proxy2.find(); 28 System.out.println("find 返回对象:" + obj2.getClass()); 29 System.out.println("----------------------------------"); 30 proxy2.update(); 31 32 //方法三 33 System.out.println("============ 方法三 =============="); 34 final AdminService target3 = new AdminServiceImpl(); 35 AdminService proxy3 = (AdminService) Proxy.newProxyInstance(target3.getClass().getClassLoader(), target3.getClass().getInterfaces(), new InvocationHandler() { 36 @Override 37 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 38 System.out.println("判断用户是否有权限进行操作"); 39 Object obj = method.invoke(target3, args); 40 System.out.println("记录用户执行操作的用户信息、更改内容和时间等"); 41 return obj; 42 } 43 }); 44 45 Object obj3 = proxy3.find(); 46 System.out.println("find 返回对象:" + obj3.getClass()); 47 System.out.println("----------------------------------"); 48 proxy3.update(); 49 } 50 }
结果如下:
1 ============ 方法一 ============== 2 代理的目标对象:class com.proxy.java.service.AdminServiceImpl 3 代理对象:class com.sun.proxy.$Proxy0 4 判断权限 5 查找数据 6 操作结束 7 find 返回对象:class java.lang.Object 8 ---------------------------------- 9 判断权限 10 更新数据 11 操作结束 12 ============ 方法二 ============== 13 判断权限 14 查找数据 15 操作结束 16 find 返回对象:class java.lang.Object 17 ---------------------------------- 18 判断权限 19 更新数据 20 操作结束 21 ============ 方法三 ============== 22 判断用户是否有权限进行操作 23 查找数据 24 记录用户执行操作的用户信息、更改内容和时间等 25 find 返回对象:class java.lang.Object 26 ---------------------------------- 27 判断用户是否有权限进行操作 28 更新数据 29 记录用户执行操作的用户信息、更改内容和时间等
4、Cglib代理
JDK动态代理要求target对象是一个接口的实现对象,假如target对象只是一个单独的对象,并没有实现任何接口,这时候就会用到Cglib代理(Code Generation Library),即通过构建一个子类对象,从而实现对target对象的代理,因此目标对象不能是final类(报错),且目标对象的方法不能是final或static(不执行代理功能)。
Cglib依赖的jar包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.10</version> </dependency>
目标对象类AdminCglibService.java
1 public class AdminCglibService { 2 public void update(){ 3 System.out.println("修改数据"); 4 } 5 6 public Object find() { 7 System.out.println("查看管理系统数据"); 8 return new Object(); 9 } 10 }
代理类AdminServiceCglibProxy.java
1 import org.springframework.cglib.proxy.Enhancer; 2 import org.springframework.cglib.proxy.MethodInterceptor; 3 import org.springframework.cglib.proxy.MethodProxy; 4 5 import java.lang.reflect.Method; 6 7 8 public class AdminServiceCglibProxy implements MethodInterceptor { 9 private Object target; 10 11 public AdminServiceCglibProxy(Object target){ 12 this.target = target; 13 } 14 15 public Object getProxyInstance(){ 16 Enhancer en = new Enhancer(); 17 en.setSuperclass(target.getClass()); 18 en.setCallback(this); 19 return en.create(); 20 } 21 22 @Override 23 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 24 System.out.println("判断用户是否有权限进行操作"); 25 Object obj = method.invoke(target); 26 System.out.println("记录用户执行操作的用户信息、更改内容和时间等"); 27 return obj; 28 } 29 }
Cglib代理测试类CglibProxyTest.java
1 public class CglibProxyTest { 2 public static void main(String[] args) { 3 4 AdminCglibService target = new AdminCglibService(); 5 AdminServiceCglibProxy proxyFactory = new AdminServiceCglibProxy(target); 6 AdminCglibService proxy = (AdminCglibService)proxyFactory.getProxyInstance(); 7 8 System.out.println("代理对象:" + proxy.getClass()); 9 10 Object obj = proxy.find(); 11 System.out.println("find 返回对象:" + obj.getClass()); 12 System.out.println("----------------------------------"); 13 proxy.update(); 14 } 15 }
结果为:
1 代理对象:class com.study.javamultithread.service.AdminCglibService$$EnhancerByCGLIB$$48403b74 2 判断用户是否有权限进行操作 3 查看管理系统数据 4 记录用户执行操作的用户信息、更改内容和时间等 5 find 返回对象:class java.lang.Object 6 ---------------------------------- 7 判断用户是否有权限进行操作 8 修改数据 9 记录用户执行操作的用户信息、更改内容和时间等
总结
理解上述Java代理后,也就明白Spring AOP的代理实现模式,即加入Spring中的target是接口的实现时,就使用JDK动态代理,否是就使用Cglib代理。
Spring也可以通过<aop:config proxy-target-class="true">强制使用Cglib代理,使用Java字节码编辑类库ASM操作字节码来实现,直接以二进制形式动态地生成 stub 类或其他代理类,性能比JDK更强。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)