GOF23(6)之代理模式(结构型模式)

人走茶凉 提交于 2020-02-06 03:45:15

1、核心作用

通过代理,控制对对象的访问。可以详细控制访问某个对象的方法,在调用这个方法之前做前置处理,调用这个方法之后做后置处理。Spring的AOP就是利用代理模式进行实现的。

2、代理模式的角色

抽象角色:定义代理角色和真实角色的公共对外方法;

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色使用;

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。实现了将统一的流程控制放到了代理角色中处理。代理角色持久真实角色的引用

3、应用场景

安全代理:屏蔽对真实对象的直接访问;

远程代理:通过代理类处理远程方法调用(RMI);

延迟加载:先加载轻量级的代理对象,真正需要的时候才加载真实的对象。比如开发一个大文档查看软件,里面有大量的图片,有可能图片的大小有100M,在打开文件时不可能将所有的图片都显示出来,于是可以使用代理模式,当需要查看图片时,用proxy进行大图片的打开。

4、分类

代理可以分为静态代理和动态代理;

静态代理:静态生产代理类。

动态代理:动态生成代理类。

实现动态代理的技术:JDK自带的动态代理,javaassist字节码操作库实现,CGLIB,ASM(底层使用指令,可维护性较差)。

5、代码实现

5.1 静态代理

5.1.1 创建抽象角色

package com.zdw.proxy.staticProxy;

/**
 * 抽象角色,这里比作歌星
 */
public interface Star {
    /**
     * 面谈
     */
    public void confer();

    /**
     * 签合同
     */
    public void signContract();

    /**
     * 唱歌
     */
    public void sing();

    /**
     * 收钱
     */
    public void collectMoney();
}

5.1.2 创建真实角色

package com.zdw.proxy.staticProxy;

/**
 * 真实角色,真正要做的事情就是唱歌
 */
public class ZhouJieLun implements Star {
    @Override
    public void confer() {

    }

    @Override
    public void signContract() {

    }

    @Override
    public void sing() {
        System.out.println("我是周杰伦,我会唱歌,而且很好听。。。");
    }

    @Override
    public void collectMoney() {

    }
}

5.1.3 创建代理角色

package com.zdw.proxy.staticProxy;

/**
 * 经纪人,相当于是代理角色,除了不会唱歌,其他的事情都是我干的
 */
public class JingjiRen implements Star {
    //经纪人知道周杰伦的联系方式,相当于代理对象持有真实角色的引用
    private Star star;

    //构造方法给真实角色赋值
    public JingjiRen(Star star) {
        this.star = star;
    }

    @Override
    public void confer() {
        System.out.println("经纪人和第三方公司面谈");
    }

    @Override
    public void signContract() {
        System.out.println("经纪人和第三方公司签合同");
    }

    @Override
    public void sing() {
        //调用真实角色唱歌
        star.sing();
    }

    @Override
    public void collectMoney() {
        System.out.println("经纪人负责收钱。。。");
    }
}

5.1.4 测试

package com.zdw.proxy.staticProxy;

public class TestStaticProxy {
    public static void main(String[] args) {
        Star zhouJieLun = new ZhouJieLun();//真实角色
        Star proxy = new JingjiRen(zhouJieLun);//代理角色
        proxy.confer();
        proxy.signContract();
        proxy.sing();
        proxy.collectMoney();
    }
}

打印结果:

经纪人和第三方公司面谈
经纪人和第三方公司签合同
我是周杰伦,我会唱歌,而且很好听。。。
经纪人负责收钱。。。

 

5.2 动态代理

这里使用的是JDK自带的动态代理来实现,主要用到的是两个类:java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler

Proxy:动态生成代理类和对象;

InvocationHandler:它里面的invoke方法实现对真实对象的代理访问,每次通过Proxy的newProxyInstance生成代理类对象时,都要指定对应的处理器对象。

5.2.1 创建抽象接口

package com.zdw.proxy.dynamicProxy;

public interface Singer {
    public void sing();
}

5.2.2 创建真实角色

package com.zdw.proxy.dynamicProxy;

public class ZhouJieLun implements Singer {
    @Override
    public void sing() {
        System.out.println("我唱的七里香很好听哦。。。");
    }
}

5.2.3 创建处理器

package com.zdw.proxy.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 处理器,动态代理的对象会先执行invoke方法
 */
public class SingerHandler implements InvocationHandler {

    private Singer singer;//持有真实对象的引用

    public SingerHandler(Singer singer) {
        this.singer = singer;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我会先帮歌手去签合同");
        //调用真实对象来唱歌
        Object o = method.invoke(singer, args);
        System.out.println("唱歌之后,我负责来帮歌手收钱");
        return o;
    }
}

5.2.4 测试

package com.zdw.proxy.dynamicProxy;

import java.lang.reflect.Proxy;

public class TestDynamicProxy {
    public static void main(String[] args) {
        Singer singer = new ZhouJieLun();
        SingerHandler singerHandler = new SingerHandler(singer);
        Singer proxy = (Singer) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Singer.class}, singerHandler);
        proxy.sing();
    }
}

运行结果:

我会先帮歌手去签合同
我唱的七里香很好听哦。。。
唱歌之后,我负责来帮歌手收钱

6、应用场景

Struts2中拦截器的实现;

数据库连接池关闭处理;

Hibernate中延迟加载的实现;

MyBatis中实现拦截器插件;

AspectJ的实现;

Spring中AOP的实现:日志拦截和声明式事务管理;

RMI远程方法调用。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!