Java设计模式——代理模式

有个故事很长很长............................然后就没了

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象以便向外界提供功能接口

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

——摘自菜鸟教程

 简单的静态代理:

下面代码演示:

背景:玛蒂达收定金,莱昂动手,玛蒂达收余款,然后对话两句。根据背景编写代码

创建接口杀手职业

/**
 * 杀手职业
 *
 * @author wanghao
 * @version C10 2018年5月10日
 * @since SDP V300R003C10
 */
interface Killer
{
    void kill();
}

创建莱昂杀手类

/**
 * 杀手:莱昂登场
 *
 * @author wanghao
 * @version C10 2018年5月10日
 * @since SDP V300R003C10
 */
class Léon implements Killer
{
    @Override
    public void kill()
    {
        System.out.println("莱昂杀了一只鸡");
    }
    
    public void léonSay(){
        System.out.println("莱昂:总是如此");
    }
}

创建代理类:收钱人玛蒂达

/**
 * 创建代理类:收钱人玛蒂达
 *
 * @author wanghao
 * @version C10 2018年5月10日
 * @since SDP V300R003C10
 */
class Mathilda implements Killer
{
    private Léon léon;
    Mathilda()
    {
        if (léon == null)
        {
            léon = new Léon();
        }
    }
    @Override
    public void kill()
    {
        getDeposit();
        léon.kill();
        getBalance();
        mathildaSay();
        léon.léonSay();
    }
    public void getDeposit() {
        System.out.println("收取定金");
    }
    
    public void getBalance() {
        System.out.println("收取余款");
    }
    public void mathildaSay() {
        System.out.println("\n玛蒂达:人生总是这么痛苦吗?还是只有小时候是这样");
    }
}

然后然后然后然后:简单的测试类

public static void main(String[] args)
    {
        Killer killer = new Mathilda();
        killer.kill();
    }

面向用户的代码就是这个,你能看出来是谁杀了人吗?看下执行结果

代理模式就是为了更好的隐藏被代理者,然后代理者可以内部控制被代理者。下面画张丑图可以意会一下:

 

代理模式与之前装饰者模式的区别也在于,代理模式隐藏代理者并且控制代理者,装饰者模式却是对被装饰者的一个扩展或者增强。

可点击进入本人分享的装饰者模式 Java设计模式——装饰者模式

说到代理就要说说静态代理和动态代理了,上面是静态代理,简单的可以更好理解这种模式,然后下面说下动态代理,动态代理举出两个动态代理的例子吧:

JDK代理:

还是使用上面的杀手职业类和杀手莱昂类。然后写一个动态代理工厂类

注:JDK动态代理,被代理类需要实现接口,这是JDK动态代理的要求。

/**
 * 简化版动态代理
 *
 * @author wanghao
 * @version C10 2018年5月10日
 * @since SDP V300R003C10
 */
class ProxyFactory
{
    public static  <T> Object getProxy(Class<T> t)
    {
        return Proxy
            .newProxyInstance(
                t.getClassLoader(), 
                t.getInterfaces(), 
            new InvocationHandler()
            {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable
                {
                    System.out.println("收取定金");
                    Object result = method.invoke(t.newInstance(), args);
                    System.out.println("收取全款");
                    return result;
                }
            });
    }
}

 

因为想写简介,就没有持有目标对象了。测试代码如下:

Killer killer = (Killer)ProxyFactory.getProxy(Léon.class);
killer.kill();

不是我懒,真的测试只需要两句代码就好了,看打印吧:

如果觉得上面动态代理工厂不容易理解,那就拆分了上面工厂代理代码,仿照静态的模板写一个动态代理工厂如下:

 

/**
 * 模板化JDK动态代理模式
 *
 * @author wanghao
 * @version C10 2018年5月10日
 * @since SDP V300R003C10
 */
class ProxyFactory2<T>
{
    //持有目标对象
    private T t;
    
    //因为不知道代理者是谁,所以需要传入代理者
    ProxyFactory2(T t){
        this.t = t;
    }
    //方法实现
    public  Object getProxy()
    {
        return Proxy
            .newProxyInstance(
                t.getClass().getClassLoader(), 
                t.getClass().getInterfaces(), 
            new InvocationHandler()
            {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable
                {
                    System.out.println("收取定金");
                    Object result = method.invoke(t, args);
                    System.out.println("收取全款");
                    return result;
                }
            });
    }
}

是不是更好理解了呢,

Cglib代理:

cglib代理,又称子类代理,跟静态代理和JDK代理最大的区别就是静态与JDK都要实现接口,而cglib却不需要实现接口,创建的是目标代理的子类,代理目标对象的。

cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 

 注意:

  • 因为是子类代理,所以不能把目标类声明为final,因为加了final之后就不能被继承了。
  • 目标对象被代理的方法是静态或者final的则不会被代理,这方面可自行去看多态。

 代码如下,首先创建一个不实现接口的莱昂杀手,当然也可以实现接口

/**
 * 杀手:莱昂登场
 * 不实现接口的莱昂
 *
 * @author wanghao
 * @version C10 2018年5月10日
 * @since SDP V300R003C10
 */
class Léon 
{
    public void kill()
    {
        System.out.println("莱昂杀了一只鸡");
    }
    
}

然后创建代理工厂类:

 因为我用的Spring的包,所以引入的是Spring里面的Cglib代理包如下引入:

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

代码如下:

/**
 * Cglib动态代理工厂类
 *
 * @author wanghao
 * @version C10 2018年5月10日
 * @param <T>
 * @since SDP V300R003C10
 */
class CglibProxyFactory<T> implements MethodInterceptor
{
    //被代理或者目标对象
    private T t;
    //传入被代理对象
    CglibProxyFactory (T t){
        this.t = t;
    }
    //给被代理者创建一个代理对象
    public T getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(t.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return (T)en.create();
    }
    
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy)
        throws Throwable
    {
        System.out.println("Cglib动态代理:收取定金");
        Object result = method.invoke(t, args);
        System.out.println("Cglib动态代理:收取全款");
        return result;
    }
}

测试类如下:

好吧,依然两句测试类,本篇文章测试类好像都是两句,哈哈。

Léon léonProxy =  new CglibProxyFactory<Léon>(new Léon()).getProxyInstance();
léonProxy.kill();

下面看测试结果吧:

这就是Cglib动态代理。

代理模式到这里就完结了,自己理解也是这些。

代理模式与装饰者模式的区别

再次说一下代理模式与装饰者模式的区别,免得大家混淆,因为目标不同所以就有不同的实现

1. 代理模式隐藏代理者并且控制代理者,

2. 装饰者模式却是对被装饰者的一个扩展或者增强。

 

posted @ 2018-05-10 16:18  苦心明  阅读(183)  评论(0)    收藏  举报