【设计模式】五、代理模式(10分钟悄悄搞定)

it2024-10-23  38

目录

一、什么是代理模式?二、为什么要用代理模式?优点缺点 三、静态代理1、创建接口2、实现接口3、创建代理类4、测试5、总结 四、动态代理1、JDK代理模式1.1 创建代理类1.2 测试1.3 探究原理 2、CGLIB2.1 maven 项目需要进入2.2 创建对象2.3 创建代理类2.4 测试类2.5 CGLIB总结 3 JDK与CGLIB 对比 Spring中的代理

一、什么是代理模式?

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。 举个栗子: 我们要买房子,虽然可以自己一个小区一个小区去找,但是太浪费时间了,于是我们直接找一个中介,中介帮我们买。这个中介就是我们说的代理。

二、为什么要用代理模式?

优点

隔离,代理模式将代理对象与真实被调用对象分离。降低系统的耦合性,拓展性好可以保护目标对象可以增强目标对象的功能

缺点

增加了系统的复杂度增加了请求对象,降低了系统的处理效率

三、静态代理

废话不多说,代码说明一切

1、创建接口

public interface BuyHouse { void buyHouse(); }

2、实现接口

public class BuyHouseImpl implements BuyHouse { @Override public void buyHouse() { System.out.println("我要买房"); } }

3、创建代理类

public class Proxy implements BuyHouse { private BuyHouse subject; public Proxy(BuyHouse subject) { this.subject = subject; } private void after() { System.out.println("调用之前"); } private void before() { System.out.println("调用之后"); } @Override public void buyHouse() { before(); subject.buyHouse(); after(); } }

4、测试

public class Test { public static void main(String[] args) { Proxy proxy = new Proxy(new BuyHouseImpl()); proxy.buyHouse(); } }

5、总结

静态代理类有代理模式的优点但每一个对象创建一个代理对象,成本太高。

四、动态代理

实现动态代理,目前介绍两种方式。

JDK自带的代理CGLIB提供的类库

1、JDK代理模式

1.1 创建代理类

public class DynamicProxyHandler implements InvocationHandler { private Object object; public Object getInstance(Object o) { this.object = o; Class<?> clazz = o.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("买房前准备"); Object result = method.invoke(object, args); System.out.println("买房后装修"); return result; } } 注意Proxy.newProxyInstance()方法接受三个参数: ClassLoader loader: 指定当前目标对象使用的类加载器, Class<?>[] interfaces: 指定目标对象实现的接口的类型 InvocationHandler: 指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

1.2 测试

DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(); BuyHouse proxyBuyHouse = (BuyHouse) dynamicProxyHandler.getInstance(new BuyHouseImpl()); proxyBuyHouse.buyHouse();

1.3 探究原理

在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。我们很好奇,运行时的代理类究竟什么样子。我们将运行时生成的代理类输出出来。

// 生成代理类 byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{BuyHouse.class}); try { FileOutputStream os = new FileOutputStream("E://$Proxy.class"); os.write(bytes); os.close(); } catch (Exception e) { e.printStackTrace(); }

反编译 $Proxy.class 文件

public final class $Proxy0 extends Proxy implements BuyHouse { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void buyHouse() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.machuxin.course.patterns.proxy.jdklib.BuyHouse").getMethod("buyHouse"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }

分析代码可知,生成的代理对象继承了Proxy类,实现了BuyHouse接口。重写了buyHouse。调用链路为 $Proxy.bugHouse -> DynamicProxyHandler.invoke - > BuyHouseImpl.bugHouse

2、CGLIB

2.1 maven 项目需要进入

<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>

2.2 创建对象

public class BuyHouseCG { public void buyHouse() { System.out.println("我要买房"); } }

2.3 创建代理类

public class CglibProxy implements MethodInterceptor { public Object getInstance(Class<?> clazz){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("买房前准备"); Object result = methodProxy.invokeSuper(o, objects); System.out.println("买房后装修"); return result; } }

2.4 测试类

public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); BuyHouseCG buyHouseCglibProxy = (BuyHouseCG) cglibProxy.getInstance(BuyHouseCG.class); buyHouseCglibProxy.buyHouse(); }

2.5 CGLIB总结

CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高CGLIB创建代理对象时所花费的时间却比JDK多得多CGLIB 采用继承的方式实现代理 关于为什么性能高:CGLIB采用了fastClass机制,为代理类和被代理类各生成一个类,为类中每个方法分配一个index,这个index当做入参,FastClass可以直接定位到要调用的方法并且直接调用。JDK则需要通过反射去调用。

3 JDK与CGLIB 对比

CGLIB是通过继承,JDK是通过实现接口都在运行时生成字节码,CGLIB实现比较复杂,所以比JDK时间长JDK通过的反射,CGLIB通过FastClass直接调用,CGLIB效率高对于单例对象,CGLIB 不用频繁创建,效率更高

Spring中的代理

当Bean实现接口时,用JDK当Bean没有实现接口时,用CGLIB可以通过配置,强制使用CGLIB

项目源码: https://gitee.com/xiaowangz/learning-note

最新回复(0)