设计模式-代理模式

定义

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

角色和类图

角色

代理(Proxy)模式分为三种角色:

  • 抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  • 代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

类图

在这里插入图片描述

实现

静态代理

/**
 * @author hao
 * 抽象主题类
 */
public interface NBA {
    void playBasketBall();
}
/**
 * @author hao
 * 具体主题类
 */
public class KouBe implements NBA {
    @Override
    public void playBasketBall() {
        System.out.println("科比凌晨4点在训练...,科比进入湖人打球...");
    }
}
/**
 * @author hao
 * 代理类  与具体主题类实现了同一的接口
 */
public class KouBeAgent implements NBA{
    private KouBe kouBe = new KouBe();
    @Override
    public void playBasketBall() {
        System.out.println("科比经纪人为科比安排与湖人签订合同...");
        kouBe.playBasketBall();
    }
}

测试代码:

public class MainTest {
    public static void main(String[] args){
        KouBeAgent kouBeAgent = new KouBeAgent();
        kouBeAgent.playBasketBall();
    }
}

结果:

科比经纪人为科比安排与湖人签订合同…
科比凌晨4点在训练…,科比进入湖人打球…

静态代理模式比较简单,与装饰器模式有些类似。具体区分的话,也可以这么理解:

  • 目的不同:静态代理的目的是为了隐藏对象,而装饰器模式是为了增强对象。(通过目标对象的传入方式)

不钻牛角尖的话,其实2个模式可以看作是相同的。都是为了增强目标对象。这个大家要了解下。学设计模式不要钻牛角尖…

JDK动态代理

JDK动态代理提供了创建代理类的方法,无需我们创建代理类。

Proxy proxy = Proxy.newProxyInstance(
				ClassLoader loader, //目标类的类加载器
				Class<?> insterfaces, //目标类所实现的接口
				InvocationHandler h // 代理对象的调用处理程序);

通过上述代码,我们可以看出,JDK动态代理要求目标类必须实现接口。我们把上述静态代理的代码修改下,使用动态代理实现。
具体代码如下:

public interface NBA {
    void playBasketBall();
}
/**
 * @author hao
 * 具体主体类
 */
public class KouBe implements NBA {
    @Override
    public void playBasketBall() {
        System.out.println("科比凌晨4点在训练...,科比进入湖人打球...");
    }
}
/**
 * @author hao
 */
public class ProxyFactory {
    private KouBe kouBe;
    public ProxyFactory(KouBe kouBe){
        this.kouBe = kouBe;
    }
    public NBA getProxyInstance() {
        NBA nba = (NBA) Proxy.newProxyInstance(
                kouBe.getClass().getClassLoader(),
                kouBe.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("科比经纪人为科比安排与湖人签订合同...");
                        Object invoke = method.invoke(kouBe, args);
                        return invoke;
                    }
                }
        );
        return nba;
    }
}

测试代码:

/**
 * @author hao
 */
public class MainTest {
    public static void main(String[] args){
        ProxyFactory proxyFactory = new ProxyFactory(new KouBe());
        NBA proxyInstance = proxyFactory.getProxyInstance();
        proxyInstance.playBasketBall();
    }
}

结果:

科比经纪人为科比安排与湖人签订合同…
科比凌晨4点在训练…,科比进入湖人打球…

可以得到同样的结果。我们并没有创建代理类。这就是动态代理。上述说到JDK动态代理需要目标对象实现接口。这是为什么呢???
我们来分析下:
我们通过Arthas jad反编译下代理类 对象com.sun.proxy.$Proxy0
在这里插入图片描述
简洁的代码如下:

public final class $Proxy0 extends Proxy implements NBA {
    private static Method m3;
    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
            m3 = Class.forName("com.atguigu.mydesign.structural.proxy.jdk.NBA").getMethod("playBasketBall", new Class[0]);
    }
    //代理对象执行的目标方法
    public final void playBasketBall() {
   
            this.h.invoke(this, m3, null);
            return;
    }
}

通过代理对象的目标方法可以看出,调用代理对象的playBasketBall方法时,实际上是调用的InvocationHandler接口的实现类中的invoke方法,invoke在通过反射执行了真实对象类中的playBasketBall方法。

CGLIB动态代理

CGLIB动态代理与JDK动态代理的好处在于不需要实现接口。用CGLIB来实现上述的例子。
代码如下:

/**
 * @author hao
 */
public class ProxyFactory implements MethodInterceptor {
    private KouBe kouBe;
    public ProxyFactory(KouBe kouBi){
        this.kouBe = kouBi;
    }
    public KouBe getProxyInstance(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(kouBe.getClass());
        enhancer.setCallback(this);
        return (KouBe) enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("科比经纪人为科比安排与湖人签订合同...");
        method.invoke(kouBe, objects);
        return null;
    }
}
/**
 * @author hao
 * 具体主体类
 */
public class KouBe {
    public void playBasketBall() {
        System.out.println("科比凌晨4点在训练...,科比进入湖人打球...");
    }
}

测试结果:

科比经纪人为科比安排与湖人签订合同…
科比凌晨4点在训练…,科比进入湖人打球…

CGLIB动态代理是通过创建一个具体类的子类。然后通过字节码的方式操作调用父类方法时拦截处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值