资讯专栏INFORMATION COLUMN

设计模式之代理模式

xuweijian / 3132人阅读

摘要:下面我们通过玩英雄联盟代练的例子来说明下登录游戏赢下了一局英雄联盟,获得了金币测试结果登录游戏赢下了一局英雄联盟,获得了金币可以这样理解,自己写代理类的方式就是静态代理。

前言

刚上大学那会,英雄联盟火的一塌糊涂,当时每天都想着升到30级开启排位之旅。可是升到30级需要大把的时间不说,这时候匹配到的人,水平过于参差不齐,问候你全家的事经常发生,那个时候就想要是能有个代练帮我升到30级该多好啊.....下面我们就通过代码的方式找下代练-..-

什么是代理模式
为其他对象提供一种代理以控制对这个对象的访问
uml类图

代理模式是一个使用率非常高的模式,像spring的aop,struts2的Form元素映射都采用了代理模式。下面我们来看下类图中三种角色的具体定义:

subject:抽象主题,可以是接口也可以是抽象类,用来进行业务定义。

realsubject:真实主题类,用来实现真正的业务逻辑。

proxy:代理类,也叫委托类。它负责控制对真实主题类访问的控制,将所有抽象主题定义的方法委托给realsubject类实现,并在这个过程加上预处理和善后工作,已达到增强功能的目的.

静态代理

静态代理模式其实就是在类设计阶段就将代理类考虑在内,而不是和动态代理和cglib代理一样动态生成代理类。下面我们通过玩英雄联盟代练的例子来说明下:

public interface IGame {

    void login();

    void playLOL();

}
public class IGamePlayer implements IGame {

    private String name;

    public IGamePlayer(String name) {
        this.name = name;
    }

    @Override
    public void login() {
        System.out.println(name+"登录游戏");
    }

    @Override
    public void playLOL() {
        System.out.println(name+"赢下了一局英雄联盟,获得了100金币");
    }
}
public class IGameProxy implements IGame {

    private IGamePlayer iGamePlayer;

    public IGameProxy(IGamePlayer iGamePlayer) {
        this.iGamePlayer = iGamePlayer;
    }

    @Override
    public void login() {
        iGamePlayer.login();
    }

    @Override
    public void playLOL() {
        iGamePlayer.playLOL();
    }
}
public class Client {

    public static void main(String[] args) {
        IGameProxy iGameProxy = new IGameProxy(new IGamePlayer("bin"));
        iGameProxy.login();
        iGameProxy.playLOL();
    }
}
测试结果:
bin登录游戏
bin赢下了一局英雄联盟,获得了100金币

可以这样理解,自己写代理类的方式就是静态代理。创建代理类和真实主题类,用代理类控制主题类的访问。游戏代练登录账号,打游戏升级,从而节省我的时间。

动态代理

动态代理就是在设计和实现阶段不需要关心代理谁,而是在运行时期才去指定代理哪个对象。spring的核心之一就是aop,俗称面向切面编程,其核心就是采用了动态代理机制。

jdk动态代理

保持IGAME接口和业务逻辑类不变。

//通知(这个例子用来统计登录接口的耗时)
 interface IAdvice {
     void execute();
}
//前置通知
public class BeforeAdvice implements IAdvice {

    @Override
    public void execute() {
        System.out.println("执行前时间:"+System.currentTimeMillis());
    }
}
//后置通知
public class AfterAdvice implements IAdvice {

    @Override
    public void execute() {
        System.out.println("执行后时间:"+System.currentTimeMillis());
    }
}
public class DynamicProxy{

    private Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    public Object getInstance(ClassLoader classLoader, Class[] interfaces){
        return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //连接点
                if (method.getName().startsWith("login")){
                    new BeforeAdvice().execute();
                }
                Object objec=method.invoke(target,args);
                if (method.getName().startsWith("login")){
                    new AfterAdvice().execute();
                }
                return objec;
            }
        });
    }
}
public class Client {

    public static void main(String[] args) {
        IGamePlayer gamePlayer =  new IGamePlayer("bin");
        DynamicProxy dynamicProxy = new DynamicProxy(gamePlayer);
        IGame proxy= (IGame) dynamicProxy.getInstance(gamePlayer.getClass().getClassLoader(),gamePlayer.getClass().getInterfaces());
        proxy.login();
        proxy.playLOL();
    }
}
测试结果:
执行前时间:1513498759281
bin登录游戏
执行后时间:1513498759282
bin赢下了一局英雄联盟,获得了100金币

在上面的例子中,我引用了一些aop的术语,例如连接点,通知。实现了一个非常简单的面向切面编程,由项目经验的可以看下springaop关于事务的配置,就会明白这样配置的含义了。
jdk动态代理是面向接口的,也就是说代理对象是根据目标对象的所有接口决定的。到底是怎么实现的呢,我们来看下这一段代码:

public Object getInstance(ClassLoader classLoader, Class[] interfaces){
        return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //连接点
                if (method.getName().startsWith("login")){
                    new BeforeAdvice().execute();
                }
                Object objec=method.invoke(target,args);
                if (method.getName().startsWith("login")){
                    new AfterAdvice().execute();
                }
                return objec;
            }
        });
    }

Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler()):这个方法是重新生成了一个对象,通过类加载器和该类所有的接口生成。当前生成的方法都是空的,具体逻辑都会由InvocationHandler来实现。InvocationHandler我采用了内部类的方式来实现,具体逻辑都是通过invoke来访问目标对象。其调用流程就是:client-->DynamicProxy-->InvocationHandler-->IGamePlayer .

cglib代理

jdk提供的动态代理是通过接口实现的,那么cglib就是通过生成目标类的子类实现的。假如你的目标类没有实现接口,又想使用动态代理,那么cglib是你的不二选择。

Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)。Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.但是有一点需要额外注意,若目标类是final或者目标方法是static和final的,则不会被cglib方法拦截

使用cglib需要引入cglib包或者spring-core(spring核心功能已经集成cglib)

public class IGamePlayer{

    private String name;

    public IGamePlayer(String name) {
        this.name = name;
    }

    public IGamePlayer() {
    }

    public void login() {
        System.out.println(name+"登录游戏");
    }

    public void playLOL() {
        System.out.println(name+"赢下了一局英雄联盟,获得了100金币");
    }
}
public class DynamicProxy implements MethodInterceptor {

    private Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    public Object getProxyInstance(){

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(target.getClass());

        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //模拟事务
        System.out.println("事务开始了");
        method.invoke(target,objects);
        System.out.println("事务结束了");
        return null;
    }
}
public class Client {

    public static void main(String[] args) {
        IGamePlayer iGamePlayer = new IGamePlayer("bin");
        DynamicProxy dynamicProxy = new DynamicProxy(iGamePlayer);
        IGamePlayer proxy = (IGamePlayer) dynamicProxy.getProxyInstance();
        proxy.login();
        proxy.playLOL();
    }
}
测试结果:
事务开始了
bin登录游戏
事务结束了
事务开始了
bin赢下了一局英雄联盟,获得了100金币
事务结束了

如例子,我们定义了一个目标对象类,没有实现接口,通过cglib的Enhancer工具类指定父类和回调,创建代理类。实现MethodInterceptor,重写intercept,相当于jdk动态代理的invoke。

总结

代理模式分为静态代理和动态代理。最大的区别就是静态代理是自己写代理,而动态代理是通过运行时动态的生成代理类。动态代理是springaop的核心,又分为jdk代理和cglib代理,前者通过接口生成代理后者通过定义父类的子类来生成代理(类不能为final,方法不能为static和final)。
代理模式或许是大家接触的最多的模式,有了springaop和aspectj这样优秀的工具,我们拿来定义即可。在学习aop框架的时候,要先弄清一些专业名词,切面、切入点、通知、织入,理解这些名词,知道代理模式的原理,学起aop框架就会游刃有余了。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70835.html

相关文章

  • PHP设计模式代理模式

    摘要:虚拟代理如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。 概念 代理模式(Proxy Pattern) :一种对象结构型模式。给某一个对象提供一个代理,并由代理对象控制对原对象的引用。 UML showImg(https://seg...

    zhisheng 评论0 收藏0
  • 设计模式代理模式

    摘要:代理模式的定义来源于百度百科为其他对象提供一种代理以控制对这个对象的访问。二来源大话设计模式三例子实现现在根据上面购票代理的场景来实现例子接口,定义真实火车站,还有代理提供的服务。一、定义       在网络不发达之前,我们买火车票,通常都需要跑到火车站去买。这对于我们来说可能有些麻烦,偶然有一天,你发现你楼下有一家便利店居然能买火车票,这就方便很多。其实啊,便利店并不提供火车服务,也没有权...

    enda 评论0 收藏0
  • Spring框架我见(二)——代理模式

    摘要:聊完了工厂模式,下面我们来说框架中的另一个核心设计模式代理模式。这里的外卖小哥就相当于是我们的代理。主要分为代理和代理。 聊完了工厂模式,下面我们来说Spring框架中的另一个核心设计模式——代理模式(Proxy Pattern)。 代理模式 大家可以先不用看概念,先举个吃饭的例子:比如说我们想吃饭,我们可以选择自己做饭吃、去饭店吃、叫外卖吃。如果我们选择自己做着吃,我们就需要去买菜、...

    oneasp 评论0 收藏0
  • 设计模式代理模式

    摘要:虚拟代理虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。主要参考设计模式与开发实践 设计模式 在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。 这是在《设计模式》一书中对设计模式的定义。在软件开发过程中,我们可能会遇到过这样的情况,我们现在发现一个问题,和以前的某个问题很相似,几乎可以用统一套解决方案,而且我们还发现,在某个条件下,这个解决方案几乎就是通用的,...

    Gilbertat 评论0 收藏0
  • Javascript设计模式——代理模式

    摘要:最近在读设计模式与开发实践,在这里把文中的各种设计模式写出来,以便加深记忆,也可以分享给初学者。经纪人可以全权代表明星和客户谈判,最后把谈判结果给明星,明星决定签约与否。这也违反了面向对象设计原则中的单一职责原则。 最近在读《javascript设计模式与开发实践》,在这里把文中的各种设计模式写出来,以便加深记忆,也可以分享给初学者。如果你不了解设计模式,那么强烈推荐你阅读一下这本书,...

    cuieney 评论0 收藏0
  • JS设计模式代理模式

    摘要:什么是代理模式代理模式,类似于明星的经纪人,想要拜访明星,需要先通过经纪人的沟通。不同于装饰器,那种动态加载一个对象,可以说在代理模式当中,代理是早已既定的。又称单一功能原则,面向对象五个基本原则之一。 什么是代理模式 代理模式,类似于明星的经纪人,想要拜访明星,需要先通过经纪人的沟通。而在JS当中,如果想访问一个类,需要通过另一个类来间接访问 。不同于装饰器,那种动态加载一个对象,可...

    widuu 评论0 收藏0

发表评论

0条评论

xuweijian

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<