23种设计模式-代理模式

  1. 游戏

书中用游戏来引入代理模式,游戏大家都玩过,基本套路就是打怪升级,我们把这段打游戏的过程系统化,非常简单的一个过程,如图12-1:


12-1

非常简单,定义一个接口IGamePlayer,表示所有喜爱网络游戏的玩家,然后定义一个具体的实现类GamePlayer,实现每个游戏爱好者玩游戏要执行的功能。定义了三个方法,分别是在网络游戏中常用的功能:登录游戏、杀怪和升级,其实现代码如下:

//玩家接口
public interface IGamePlayer {
    void login(String user,String password);
    void killBoss();
    void upgrade();
}
//玩家实现类
public class GamePlayer implements IGamePlayer {
    private String name;
    public GamePlayer(String name){
        this.name = name;
    }

    @Override
    public void login(String user, String password) {
        System.out.println("登入名为"+user+"的用户"+this.name+"登入成功");
    }

    @Override
    public void killBoss() {
        System.out.println(this.name+"击杀boss");
    }

    @Override
    public void upgrade() {
        System.out.println(this.name+"升级");
    }
}
//场景类
public class Client {
    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("张三");
        System.out.println("开始时间是:"+new Date());
        player.login("zhangSan","password");
        player.killBoss();
        player.upgrade();
        System.out.println("结束时间是:"+new Date());
    }
}

大家玩游戏都知道,升级是一个很痛苦的过程,尤其是一些需要肝到爆的游戏,于是就有了各种外挂,然后被封号;还有一些相关的的职业诞生了,那就是游戏代练,把账号交给代练给我们升级,那我们来修改一下类图12-2所示:


12-2

在类图中增加了一个GamePlayerProxy类来代表游戏代练者,他本质也是一个游戏玩家,因此同样继承IGamePlayer接口,代码如下:

//代练类
public class GamePlayerProxy implements IGamePlayer {
    private IGamePlayer gamePlayer;
    public GamePlayerProxy(IGamePlayer gamePlayer){
        this.gamePlayer = gamePlayer;
    }

    @Override
    public void login(String user, String password) {
        this.gamePlayer.login(user,password);
    }

    @Override
    public void killBoss() {
        this.gamePlayer.killBoss();
    }

    @Override
    public void upgrade() {
        this.gamePlayer.upgrade();
    }
}
//场景类
public class Client {
    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("张三");
        IGamePlayer proxy = new GamePlayerProxy(player);
        System.out.println("开始时间是:"+new Date());
        proxy.login("zhangSan","password");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间是:"+new Date());
    }
}

你自己没有玩游戏,将号交给代练,代练帮你去打怪升级了,这就是代理模式。

  1. 代理模式的定义

代理模式(Proxy Pattern)是一个使用率非常高的模式,其定义如下:
Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问)
代理模式的通用类图如图12-3:

12-3

带了模式也叫委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常应用中,代理模式可以提供非常好的访问控制。先看类图中的三个角色的定义:

  • Subject抽象主题角色
    抽象主题类可以是抽象类也可以是接口,是一个普通的业务类型定义,没有特殊要求,对应例子中的IGamePlayer
  • RealSubject具体主题角色
    也叫做被委托角色、被代理角色。它才是核心业务逻辑的具体执行者,对应GamePlayer
  • Proxy代理主题角色
    也叫作委托类,代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。对应GamePlayerProxy
    首先,来看一下Subject抽象主题类的通用源码,代码如下:
public interface Subject {
    //定义一个方法
    public void request();
}

在接口中我们定义一个方法request来作为方法的代表,RealSubject对它进行实现,代码如下:

public class RealSubject implements Subject {
    @Override
    public void request() {
        //业务逻辑处理
    }
}

RealSubject是一个正常的业务实现类,代理模式的和核心就在代理类上,代码如下:

public class Proxy implements Subject {
    //被代理者
    private Subject subject = null;

    public Proxy(Subject subject){
        this.subject = subject;
    }
    @Override
    public void request() {
        this.before();
        this.subject.request();
        this.after();
    }
    //预处理
    private void before(){
        //do something
    }
    //善后处理
    private void after(){
        //do something
    }
}

在代理类中出现了before和after方法,这是一个"引子",能够引出一个崭新的编程方式。(我先猜一下是不是指的面向切面编程,spring中有个关于aop的概念,其实现方式就是用的代理模式
一个代理类可以代理多个被委托者或被代理者,因此一个代理类具体代理哪个真实主题角色,是由场景类决定的。当然,最简单的情况就是一个主题类和一个代理类,这是最简洁的代理模式。在通常情况下,一个接口只需要一个代理类就可以了,具体代理哪个实现类由高层模块代码来决定,也就是代理类的构造函数中传递被代理者,就如上例中的代理类的构造函数中传递被代理者,你要代理谁就产生该代理的实例,然后把被代理者传递进来,该模式在实际的项目应用中比较广泛。

  1. 代理模式的应用

3.1 代理模式的优点

  • 职责清晰
    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的失误,通过后期的代理完成一件事物,附带的结果就是编程简单清晰。
  • 高扩展性
    具体主题角色是随时都会发生变化,只要它实现了接口,不管它如何变化,都不会违反接口契约限制,那么代理类就完全不需要做任何修改就可以使用。符合开闭原则(发现基本上好的设计都是符合开闭原则,这是核心的原则)
  • 智能化
    在以上的例子中没有体现出来,但是在动态代理的章节中就可以看到代理的智能化。
    3.2 代理模式使用场景
    代理模式就是把核心业务抓在手上自己做,一些外围的不是很重要的工作交给代理去做,代理模式的使用场景非常多,大家可以看看springAop,这是一个非常典型的动态代理。
  1. 代理模式的扩展

4.1 普通代理
在网络上代理服务器设置分为透明代理和普通代理,是什么意思?透明代理就是用户不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说是透明的,不用知道它的存在;普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。我们设计模式中的普通代理和强制代理也是一种类似的结构,普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的,还是通过例子来看吧。
首先说普通代理,他的要求就是客户端只能访问代理角色,而不能访问真实角色,这个比较简单,还是以游戏代理为例子,我自己作为一个游戏玩家,练级的任务交给代理,也就是场景类不能再直接new一个GamePlayer对象了,它必须由GamePlayerProxy来进行场景模拟,类图12-4:


12-4

GamePlayer的构造函数增加了gamePlayer参数,而代理角色则只需要传入被代理者的名字即可,而不需要说是替哪个对象做代理,代码如下:

public class GamePlayer implements IGamePlayer {
    private String name = null;
    public GamePlayer(IGamePlayer gamePlayer,String name) throws Exception {
        if(gamePlayer == null){
            throw new Exception("不能创建真实角色!");
        }else{
            this.name = name;
        }
    }

    @Override
    public void login(String user, String password) {
        System.out.println("登入名为"+user+"的用户"+this.name+"登入成功");
    }

    @Override
    public void killBoss() {
        System.out.println(this.name+"击杀boss");
    }

    @Override
    public void upgrade() {
        System.out.println(this.name+"升级");
    }
}

在构造函数中,传递进来一个IGamePlayer对象,检查谁能创建真实的角色,当然还可以有其他的限制,比如类名必须为proxy,可以根据实际情况扩展(我看到这里,还是不太明白,这个构造函数传IGamePlayer对象干嘛,不太理解这个说法,接着看代码
GamePlayerProxy代码如下:

public class GamePlayerProxy implements IGamePlayer {
    private IGamePlayer gamePlayer = null;

    public GamePlayerProxy(String name){
        try{
            this.gamePlayer = new GamePlayer(this,name);
        }catch (Exception e){
            e.getMessage();
        }
    }

    @Override
    public void login(String user, String password) {
        this.gamePlayer.login(user,password);
    }

    @Override
    public void killBoss() {
        this.gamePlayer.killBoss();
    }

    @Override
    public void upgrade() {
        this.gamePlayer.upgrade();
    }
}

仅仅修改了构造函数,传递进来一个代理者名称,即可进行代理,在这种改造下,调用者就更简洁了,调用者只需要知道代理存在就可以了,不用知道代理了谁,场景类代码如下:

public class Client {
    public static void main(String[] args) {
        IGamePlayer proxy = new GamePlayerProxy("张三");
        System.out.println("开始时间是:"+new Date());
        proxy.login("zhangSan","password");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间是:"+new Date()); 
    }
}

现在就知道了为什么realSubject的构造函数传Subject了,普通代理就必须得知道代理类,可以不知道realSubject,并且创建realSubject是由代理类来创建的,并且无法直接创建realSubject。
在普通代理模式下,调用者只知代理而不用知道真实角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层模块没有任何影响,只要你不违反接口契约就好了,该模式非常适合对扩展性要求高的场景。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色(通过约定来限制就不需要通过在构造函数中传subject来限制了),这也是一个非常好的方案。
注意 普通代理模式的约束问题,尽量通过团队的编程规范约束,毕竟做的越多错的越多,使用技术约束的方式对系统维护是一种非常不利的因素
4.2 强制代理
强制代理在设计模式中比较另类,为什么这么说呢?一般的思维都是通过代理找到真实角色,但是强制代理却是要"强制",你必须通过真实角色找到代理角色,否则你不饿能访问。不管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理才可以访问,也就是说由真实角色管理代理角色。这么说吧,高层模块new了一个真实角色的对象,返回的却是代理,这就好比你想和一个明星沟通,并且你们是互相认识的,于是你拨通了他的电话,他说现在比较忙,你可以先和他经济人沟通;你想绕过代理,谁知道返回的还是他的代理,这就是强制代理,你可以不用知道代理的存在(这个刚好和普通代理是相反的),但是你的所作所为还是代理给你提供的,强制代理类图12-5如下:

12-5

在接口上增加一个getProxy方法,真实角色GamePlayer可以指定一个自己的代理,除了代理谁都不能访问。接口代码如下:

public interface IGamePlayer {
     void login(String user, String password);
     void killBoss();
     void upgrade();
     IGamePlayer getProxy();
}

仅仅增加一个getProxy方法,指定要访问自己必须通过哪个代理,实现类GamePlayer代码如下:

public class GamePlayer implements IGamePlayer {
    private String name = null;
    private IGamePlayer proxy = null;
    public GamePlayer(String name)  {
        this.name = name;
    }

    @Override
    public void login(String user, String password) {
        if(this.isProxy()){
            System.out.println("登入名为"+user+"的用户"+this.name+"登入成功");
        }else{
            System.out.println("请使用指定代理访问");
        }
        
    }

    private boolean isProxy() {
        if(this.proxy == null){
            return false;
        }
        return true;
    }

    @Override
    public void killBoss() {
        if(this.isProxy()){
            System.out.println(this.name+"击杀boss");
        }else{
            System.out.println("请使用指定代理访问");
        }
    }

    @Override
    public void upgrade() {
        if(this.isProxy()){
            System.out.println(this.name+"升级");
        }else{
            System.out.println("请使用指定代理访问");
        }
    }

    @Override
    public IGamePlayer getProxy() {
        if(this.proxy == null){
            this.proxy = new GamePlayerProxy(this);
        }
        return this.proxy;
    }
}

增加了一个私有方法,检查是否是自己制定的代理,是指定的代理则允许访问,否则不许访问。在看看代理角色,代码如下:

public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer;
public GamePlayerProxy(IGamePlayer gamePlayer){
this.gamePlayer = gamePlayer;
}
@Override
public void login(String user,String password){
this.gamePlayer.login(user,password);
}
@Override
public void killBoss(){
this.gamePlayer.killBoss();
}
@Override
public void upgrade(){
this.gamePlayer.upgrade();
}
 @Override
    public IGamePlayer getProxy() {
       return this;
    }
}

代理角色可以再次被代理,这里就没有继续延伸下去了,查找代理的方法就返回自己的实例。

public class Client {
    public static void main(String[] args) {
        //正常逻辑访问
        IGamePlayer player= new GamePlayer("张三");
        System.out.println("开始时间是:"+new Date());
        player.login("zhangSan","password");
        player.killBoss();
        player.upgrade();
        System.out.println("结束时间是:"+new Date()); 
       //任意代理访问
        IGamePlayer proxy = new GamePlayerProxy("张三");
        System.out.println("开始时间是:"+new Date());
        proxy.login("zhangSan","password");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间是:"+new Date()); 
       //指定代理访问
        IGamePlayer proxy = player.getProxy();
        System.out.println("开始时间是:"+new Date());
        proxy.login("zhangSan","password");
        proxy.killBoss();
        proxy.upgrade();
        player.login("zhangSan","password");
        player.killBoss();
        player.upgrade();
        System.out.println("结束时间是:"+new Date()); 
    }
}

这里有三种场景,一是正常的自己调用方法,返回没有指定代理;二是随便创建一个代理,也无法使用;三是通过玩家指定的代理才能正常使用,(*这里玩家指定了代理后,玩家自己也可以调用方法了,这个例子的逻辑并不很严谨,可能是通过团队开发规则约束吧

强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。高层模块只要调用getProxy就可以访问真实角色的所有方法,他根本不需要产生一个代理出来,代理的管理已经由真实角色自己完成了。(
这个得在实际开发中加深理解,目前不是太明白这样设计的用意,虽然这个例子只能通过被代理类指定代理,但是指定完代理后自己也可以完成业务,可以不通过代理完成业务

4.3 代理也是有个性的
一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象的基础上作增强,这种增强的本质通常是对目标对象的方法进行拦截和过滤。例如游戏代理是需要收费的,升一级需要5元钱,这个计算功能就是代理类的个性,它应该在代理接口中定义,类图12-6:


12-6

增加一个IProxy接口,其作用是计算代理的费用。我们先来看IProxy接口,代码如下:

public interface IProxy{
    public void count();
}

代理接口仅一个方法,再看看代理类的变化,代码如下:

public class GamePlayerProxy implements IGamePlayer,IProxy{
    private IGamePlayer gamePlayer = null;
    public GamePlayerProxy(IGamePlayer gamePlayer){
        this.gamePlayer = gamePlayer;
    }
@Override
public void login(String user,String password){
this.gamePlayer.login(user,password);
}
@Override
public void killBoss(){
this.gamePlayer.killBoss();
}
@Override
public void upgrade(){
this.gamePlayer.upgrade();
this.count();
}
@Override
public void count(){
System.out.println("升级费用是:150元");
}
}

实现IProxy接口,同时在升级方法中调用该方法,其他类都没有改动。
代理类不仅仅是可以有自己的运算方法,通常情况下代理类的职责并不一定单一,他可以组合其他真实角色,也可以实现自己的职责,比如计算费用。代理类可以为真实角色预处理消息、过滤消息、消息转发、事后处理消息等功能。当然一个代理类,可以代理多个真实角色,并且真实角色之间可以有耦合关系。(这个最好的例子就spring中aop相关的知识
4.4动态代理
上面的章节都是引子,动态代理才是重头戏。什么是动态代理?动态代理在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。相对来说,自己写代理类的方式是静态代理。本章节的核心部分就在动态代理上,就是我在前面一直有提到的AOP(Aspect Oriented Programming),其核心就是采用了动态代理机制,一起学习一下动态代理是怎么实现的,还是以打游戏为例,类图修改一下以实现动态代理,类图12-7:

12-7

在类图中增加了一个InvocationHandler接口和GamePlayIH类,作用就是产生一个对象的代理对象,其中InvocationHandler是JDK提供的动态代理接口,对被代理的方法进行代理。我们看程序,接口保持不变,实现类也没有变化,来看看DynamicProxy类,代码如下:

public class GamePlayerIH  implements InvocationHandler {
    //被代理者
    Class cls = null;
    //被代理的实例
    Object obj = null;
    //我要代理谁
    public GamePlayerIH(Object obj){
        this.obj = obj;
    }
    //调用别代理的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.obj,args);
        return result;
    }
}

其中invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调用。我们来详细学习一下InvocationHandler接口,动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称"我已经实现该接口下的所有方法了",那动态代理怎么才能实现被代理的接口中的方法呢?默认情况下所有方法的返回值都是空的,代理已经实现它了,但是没有任何的逻辑含义,那怎么办?通过InvocationHandler接口,所有的方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务。(本人也是一脸蒙蔽,也就猜到可能会通过反射去实现接口的方法,接着往下看看,看是怎么处理的
接下来看场景类,代码如下:

public class Client {
    public static void main(String[] args) {
        IGamePlayer player = new GamePlayer("zhangsan");
        InvocationHandler handler = new GamePlayerIH(player);
        System.out.println("开始时间是:"+new Date());
        //获得类的class loader
        ClassLoader cl = player.getClass().getClassLoader();
        //动态产生一个代理者
        IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl,new Class<?>[]{IGamePlayer.class},handler);
        proxy.login("zhangsan","123456");
        proxy.killBoss();
        proxy.upgrade();
        System.out.println("结束时间是:"+new Date());
    }
}

我们还是让代练者帮我们打游戏,但是我们既没有创建代理类,也没有实现IGamePlayer接口,这就是动态代理。(我到这里很蒙蔽,比不明白其中实现的原理,但是很神奇,这就是代码的魅力)动态代理可不仅仅就这么多类容,还有更重要的,如果想让游戏登入后发一个信息给我们,防止账号被人盗用,该怎么处理?直接修改被代理类GamePlayer的逻辑?这不是一个好办法,解决办法如下代码:

public class GamePlayerIH  implements InvocationHandler {
    //被代理者
    Class cls = null;
    //被代理的实例
    Object obj = null;
    //我要代理谁
    public GamePlayerIH(Object obj){
        this.obj = obj;
    }
    //调用被代理的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.obj,args);
        if(method.getName().equalsIgnoreCase("login")){
            System.out.println("有人在用我的账号登入!");
        }
        return result;
    }
}

我们只是在代理中增加一个判断调用的方法是否是login就可以决定是否要发送信息;有人用我的账号就发送一个信息,然后看看自己的账号是不是被人盗了,非常好的方法,这就是AOP编程。AOP编程没有使用什么新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事物、权限等都可以在系统设计阶段不用考虑,而在设计后通过AOP的方式切过去。我们来看看动态代理模型,类图12-8:


12-8

两条独立的发展线路。动态代理实现代理的职责,业务逻辑Subject是想相关的逻辑功能,两者之间没有必然的互相耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。我们先来看Subject接口,代码如下:

//抽象主题
public interface Subject {
    //业务操作
     void doSomething(String str);
}

其中doSomething是一种标识方法,可以有多个逻辑处理方法,实现类代码如下:

//真实主题
public class RealSubject implements Subject {
    @Override
    public void doSomething(String str) {
        System.out.println("do Something!--->"+str);
    }
}

重点是我们的MyInvMyInvocationHandler,代码如下:

public class MyInvocationhandler implements InvocationHandler {
    //被代理的对象
    private Object target = null;

    public MyInvocationhandler(Object object){
        this.target = object;
    }
    //代理方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行被代理的方法
        return method.invoke(this.target,args);
    }
}

所有通过动态代理实现的方法全部通过invoke方法调用。DynamicProxy代码如下:

public class DynamicProxy<T> {
    public static  <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler){
        //寻找JoinPoint连接点,AOP框架使用元数据定义
        if(true){
            //执行一个前置通知
            (new BeforeAdvice()).exec();
        }
        //执行目标并返回结果
        return (T) Proxy.newProxyInstance(loader,interfaces,handler);
    }
}

在这里插入了较多的AOP术语,如在什么地方(连接点)执行什么方法(通知)。我们在这里实现了一个简单的横切面编程,有经验的朋友可以看看AOP的配置文件就会明这段代码的意义了。我们来看通知Advice,也就是要切入的类,接口和实现代码如下:

public interface IAdvice {
    //通知只有一个方法,执行即可
    void exec();
}
public class BeforeAdvice implements IAdvice{
    @Override
    public void exec() {
        System.out.println("我是前置通知,我被执行了");
    }
}

再来看一下调用的流程,场景类代码如下:

public class Client {
    public static void main(String[] args) {
        //定义一个主题
        Subject subject = new RealSubject();
        //定义一个Handler
        InvocationHandler handler = new MyInvocationhandler(subject);
        //定义主题的代理
        Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),handler);
        //代理的行为
        proxy.doSomething("Finish");
    }
}

程序都看完了,我们回过头来看看程序是怎么实现的。在DynamicProxy类中,我们有这样的方法:

this.obj = Proxy.newProxyInstance(c.getClassLoader,c.getInterfaces,new MyInvocationHandler(_obj));

该方法是重新生成一个对象,为什么要重新生成?你要使用代理呀,注意c.getInterfaces()这句话,这是非常有意思的一句话,是说查找到该类的所有接口,然后实现接口的所有方法。当然了,方法都是空的,由谁具体负责接管?是new MyInvocationHandler(_obj)这个对象。于是我们知道一个类的动态代理是这样一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现,其动态调用过程如图12-9所示:


12-9

其实我们以上代码还能进一步的扩展,看DynamicProxy类,它是一个通用类,不具有业务意义,如果我们在产生一个实现类是不是就很有意义了呢?代码如下:

public class SubjectDynamicProxy extends DynamicProxy {
    public static <T> T newProxyInstance(Subject subject){
        //获得ClassLoader
        ClassLoader loader = subject.getClass().getClassLoader();
        //获得接口数组
        Class<?>[] classes = subject.getClass().getInterfaces();
        //获得handler
        InvocationHandler handler = new MyInvocationhandler(subject);
        
        return newProxyInstance(loader,classes,handler);
    }
}

如此扩展以后,高层模块对代理的访问就跟简单了,代码如下:

public class Client {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        Subject proxy =  SubjectDynamicProxy.newProxyInstance(subject);
        proxy.doSomething("Finish");
    }
}

是不是更简单了,那么问题来了,静态代理好像也是这么个写法,一个主题类,在创建一个代理类,然后通过代理类去执行业务。这是有区别的,注意看父类,动态代理的主要意图就是解决我们常说的"审计"问题,也就是面向切面编程,在不改变我们已有代码结构的情况下增强或控制对象的行为。

注意
要实现动态代理的首要条件是:被代理类必须实现一个接口,回想一下前面的分析(
代码中有一步不就是获取被代理类的所有接口吗)。当然了,现在也有很多技术如CGLIB可以实现不需要接口也可以实现的动态代理的方式 。
再次说明,以上的动态代理是一个通用代理框架。如果你想设计自己的AOP框架,完全可以在此基础上扩展,我们设计的是一个通用代理,只要有一个接口,一个实现类,就可以使用该代理,完成代理的所有功效

  1. 最佳实践

代理模式应用的非常广泛,达到一个系统框架、企业平台,小到代码片段、事物处理,稍不留意就用到代理模式了。这可能是大家接触最多的模式,而且有了AOP大家写代理就更加简单了,有类似SpringAop和AspectJ这样非常优秀的工具,拿来主义即可!不过,大家可以看看源代码,特别是调试的时候,只要看到类似$Proxy()这样的结构,就应该知道这是一个动态代理了。
在学习AOP框架的时候,弄清楚几个名词就成:切面(Aspect)、切入点(JoinPoint)、通知(Advice)、织入(Weave)就够了,理解这些,应用时就游刃有余了!

上面有提到了动态代理模式的实现方式,jdk是基于接口实现的也就是例子中的实现方式,我们可以看出,是通过获取所有的接口,然后通过反射执行接口中对应的方法来实现的;我去百度了一下cglib动态代理(基于继承),java是单继承的,那么用cglib的方式就限制比较多了,只能代理一个类的;另外,不管是接口还是继承,都只能代理接口或父类中的方法,被代理类自己的方法是代理无法处理的
内容来自《设计模式之禅》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容