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

一、什么是代理模式?

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

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

优点

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

缺点

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

三、静态代理

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值