Java设计模式(八)----代理模式

代理描述
1.生活中:
代理就是一个人或者一个组织代表其他人去做一件事的现实生活中的。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
2.官方:
代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用

一、静态代理
类图结构如下



在代理模式中的角色:

  ●  抽象主题角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。

  ●  真实主题角色:定义了代理对象所代表的目标对象。

  ●  代理主题角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。它可以增加一些真实主题里面没有的功能。



生活中的例子:过年加班比较忙,没空去买火车票,这时可以打个电话到附近的票务中心,叫他们帮你买张回家的火车票,当然这会附加额外的劳务费。但要清楚票务中心自己并不卖票,只有火车站才真正卖票,票务中心卖给你的票其实是通过火车站实现的。这点很重要!

上面这个例子,你就是“客户”,票务中心就是“代理角色”,火车站是“真实角色”,卖票称为“抽象角色”!

代码

抽象主题角色

//抽象角色:声明真实对象和代理对象的共同接口;
public interface  TicketManager {
    /**
     * 售票
     */
    public  void  soldTicket();
    /**
     * 改签
     */
    public void changeTicket();
    /**
     * 退票
     */
    public void returnTicket();
}

真实主题角色

public class TicketManagerImpl implements TicketManager {

    @Override
    public void soldTicket() {
        System.out.println("售票");
    }

    @Override
    public void changeTicket(){

        System.out.println("改签");
    }

    @Override
    public void returnTicket() {
        System.out.println("退票");
    }

}

代理主题角色(添加了身份验证功能)

public class StaticProxyTicketManager implements TicketManager {
    TicketManager ticketManager;//目标对象的引用

    public StaticProxyTicketManager(TicketManager ticketManager) {
        this.ticketManager = ticketManager;
    }

    @Override
    public void soldTicket() {
        checkIdentity();//前置增强
        ticketManager.soldTicket();
    }

    @Override
    public void changeTicket() {
        checkIdentity();
        ticketManager.changeTicket();
    }

    @Override
    public void returnTicket() {
        checkIdentity();
        ticketManager.changeTicket();
    }
    /**
     * 身份验证
     */
    public void checkIdentity(){
        System.out.println("身份验证--------------");
    }

}

第二个代理主题角色(添加了日志功能)

//代理类  实现同一个接口
public class LogProxy implements TicketManager {
    TicketManager ticketManager;//目标类的引用
    public LogProxy(TicketManager ticketManager){
        this.ticketManager=ticketManager;
    }
    @Override
    public void soldTicket() {
        ticketManager.soldTicket();
        log();//后置增强
    }

    @Override
    public void changeTicket() {
        ticketManager.changeTicket();
        log();
    }

    @Override
    public void returnTicket() {
        ticketManager.returnTicket();
        log();

    }
    //增强
    private void log() {
        System.out.println("日志...");

    }

}

客户端

public class Test {
    public static void main(String[] args) {
        //装饰模式   new TicketManagerImpl()  真实的目标对象
        //TicketManager tm=new StaticProxyTicketManager(new TicketManagerImpl());
        TicketManager tm=new LogProxy(new StaticProxyTicketManager(new TicketManagerImpl()));

        tm.soldTicket();
        tm.changeTicket();
        tm.returnTicket();
    }
}

结果:
身份验证————–
售票
日志…
身份验证————–
改签
日志…
身份验证————–
改签
日志…

从上面例子可以看出 客户端通过代理来购票 而代理实际上不能卖票给客户,他实际上是通过目标对象卖票给客户的,也就是说他是通过真实主题的目标对象实现给客户端卖票的功能,他只是一个中介,但我们可以在它里面增加一些功能,比如身份验证或者宣传打广告等其他的功能。



静态代理类:在程序运行前,代理类的.class文件就已经存在了,已确定被代理的对象
静态代理:
优点:对真实对象进行封装,不会修改目标类的代码。
缺点:
1.多个不同类型目标对象需要代理时,我就需要建立多个代理类,造成类的膨胀
2.代码的冗余
3.编译期加入,不够灵活



二、动态代理
描述(这个描述从网上看到的,相对比较容易理解)
动态代理(Dynamic Proxy):相比静态代理,动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现。

所谓代理,就是需要代理类和被代理类有相同的对外接口或者说成服务,所以代理类一般都必须实现了所有被代理类已实现的接口,因为接口就是制定了一系列对外服务的标准。

1.JDK实现动态代理



正因为动态代理有这样灵活的特性,所以我们在设计动态代理类(DynamicProxy)时不用显式地让它实现与真实主题类(RealSubject)相同的接口(interface),而是把这种实现推迟到运行时。

为了能让DynamicProxy类能够在运行时才去实现RealSubject类已实现的一系列接口并执行接口中相关的方法操作,需要让DynamicProxy类实现JDK自带的java.lang.reflect.InvocationHandler接口,该接口中的invoke()方法能够让DynamicProxy实例在运行时调用被代理类的“对外服务”,即调用被代理类需要对外实现的所有接口中的方法,也就是完成对真实方法的调用,Java帮助文档中称这些真实方法为处理程序。

按照上面所述,我们肯定必须先把被代理类RealSubject已实现的所有interface都加载到JVM中,不然JVM怎么能够找到这些方法呢?明白了这个道理,那么我们就可以创建一个被代理类的实例,获得该实例的类加载器ClassLoader。

所谓的类加载器ClassLoader,就是具有某个类的类定义,即类的内部相关结构(包括继承树、方法区等等)。

更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到控制被代理对象的行为的目的。请详看下面代码中的DynamicProxy类,其中必须实现的invoke()方法在调用被代理类的真实方法的前后都可进行一定的特殊操作。这是动态代理最明显的优点



类图

代码

package com.yc.spring.staticproxy;

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

public class DynamicProxyTicketManager implements InvocationHandler {
    private Object targetObject;

    /**
     * 目标的初始化方法,根据目标生成代理类
     *
     * @param targetObject
     * @return
     */
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        // 第一个参数,目标对象 的装载器
        // 第二个参数,目标接口已实现的所有接口,而这些是动态代理类要实现的接口列表
        // 第三个参数, 调用实现了InvocationHandler的对象生成动态代理实例,当你一调用代理,代理就会调用InvocationHandler的invoke方法
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),
                this);
    }

    /**
     * 反射,这样你可以在不知道具体的类的情况下,根据配置的参数去调用一个类的方法。在灵活编程的时候非常有用。
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 检查
        checkIdentity();
        Object ret = null;
        try {
            // 调用目标方法
            ret = method.invoke(targetObject, args);
            // 执行成功,打印成功信息
            log();
        } catch (Exception e) {
            e.printStackTrace();
            // 失败时,打印失败信息
            System.out.println("error-->>" + method.getName());
            throw e;
        }
        return ret;
    }

    /**
     * 身份验证
     */
    public void checkIdentity(){
        System.out.println("身份验证--------------");
    }
    public void log(){
        System.out.println("日志..." );
    }

}

客户端

public class Test {
    public static void main(String[] args) {
        DynamicProxyTicketManager dynamicProxyTicketManager=new DynamicProxyTicketManager();
        TicketManager tm=(TicketManager) dynamicProxyTicketManager.newProxyInstance(new TicketManagerImpl());

        tm.soldTicket();
        tm.changeTicket();
        tm.returnTicket();
    }
}

结果同上

优缺点

优点:

1、一个动态代理类更加简单了,可以解决创建多个静态代理的麻烦,避免不断的重复多余的代码

2、调用目标代码时,会在方法“运行时”动态的加入,决定你是什么类型,才调谁,灵活

缺点:

1、系统灵活了,但是相比而言,效率降低了,比静态代理慢一点

2、动态代理比静态代理在代码的可读性上差了一点,不太容易理解

3、JDK动态代理只能对实现了接口的类进行代理

总结
各有各的好,具体情况具体讨论



2.Cglib实现动态代理



描述(网上整理)
AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。
两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的 ,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛, 且在效率上更有优势。

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



介绍:
CGLIB的核心类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;

第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快



代码

public class CglibDynamicProxyTicketManager implements MethodInterceptor  {
    private Object targetObject;//目标对象
    /**
     * 创建代理对象
     *
     * @param targetObject
     * @return
     */
    public Object getInstance(Object targetObject) {
        this.targetObject = targetObject;
        Enhancer enhancer = new Enhancer();  // 用这个类来创建代理对象(被代理类的子类): 并设置父类;设置回调;
        enhancer.setSuperclass(this.targetObject.getClass()); // 设置被代理类作为其父类的代理目标
        // 回调方法
        enhancer.setCallback(this);  // 设置回调--当这个代理对象的方法被调用时 回调方法intercept()会被执行
        // 创建代理对象
        return enhancer.create();
    }  

    @Override
    //回调方法
    // methodProxy 代理的类的方法
    /**
     * methodProxy 会调用父类(目标对象)的被代理的方法,比如soldTicket方法等
     */
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        Object result = null;
        checkIdentity();//前置增强
        result=methodProxy.invokeSuper(obj, args); //调用新生成的cglib的代理对象 所属的父类的被代理的方法
         log();//后置增强
        return result;
    }

    /**
     * 身份验证
     */
    public void checkIdentity(){
        System.out.println("身份验证--------------");
    }
    public void log(){
        System.out.println("日志..." );
    }

}


客户端

public class Test {
    public static void main(String[] args) {
        CglibDynamicProxyTicketManager cglibdynamicProxyTicketManager=new CglibDynamicProxyTicketManager();
        //生成代理对象
        TicketManager tm=(TicketManager) cglibdynamicProxyTicketManager.getInstance(new TicketManagerImpl());

        tm.soldTicket();//当调用代理对象的被代理对象的方法时  会自动回调 代理类中的Intercept()方法
        tm.changeTicket();
        tm.returnTicket();
    }
}

结果同上

时间: 2024-07-30 03:04:56

Java设计模式(八)----代理模式的相关文章

Java设计模式之代理模式(Proxy模式)介绍_java

理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理. 设计模式中定义:为其他对象提供一种代理以控制对这个对象的访问. 为什么要使用代理模式 1.授权机制 不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进

java 程序性能优化《第二章》设计优化 2.1善用设计模式 2 代理模式

java 程序性能优化<第二章>设计优化 2.1善用设计模式 2 代理模式 代理模式也是一种很常见的设计模式.它使用代理对象完成用户请求,屏蔽用户对真实对象的访问.就如同现实中的代理一样,代理人被授权执行当事人的一些适宜,而无需当事人出面,从第三方的角度看,似乎当事人并不存在,因为他只和代理人通信.而事实上,代理人是要有当事人的授权,并且在核心问题上还需要请示当事人. 在现实中,使用代理的情况很普遍,而且原因也很多.比如,当事人因为某些隐私不方便出面,或者当事人不具备某些相关的专业技能,而需要

java设计模式---prototype(原型)模式

设计 java设计模式---prototype(原型)模式 定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.      Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,      工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求      原型对象拷贝它们自己来实施创建.      适用性:当要实例化的类是在运行时刻指定时,例如,通过动态装载:或者  为了避免创建一个与产品类层次平行的工厂类层

浅谈Java设计模式关于原型模式(Prototype)思索建议

java教程:关于Java设计模式关于原型模式(Prototype) IT信息技术http://www.52ij.com/jishu/ 首先需要弄清楚什么叫原型模式,或者说为什么要有原型模式,运用它会给我们带来什么或能解决什么问题?原型模式(Prototype)同抽象工厂模式同属于创建型模式,它主要关注于大量相同或相似对象的创建问题,应用原型模式就是先需要一个原对象,然后通过对原对象进行复制(克隆),来产生一个与原对象相同或相似的新对象.注意这里所说的对象相同不是指复制出来的副本对象与原对象是同

深入理解JavaScript系列(31):设计模式之代理模式详解

 这篇文章主要介绍了深入理解JavaScript系列(31):设计模式之代理模式详解,代理模式使得代理对象控制具体对象的引用,代理几乎可以是任何对象:文件,资源,内存中的对象,或者是一些难以复制的东西,需要的朋友可以参考下     介绍 代理,顾名思义就是帮助别人做事,GoF对代理模式的定义如下: 代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问. 代理模式使得代理对象控制具体对象的引用.代理几乎可以是任何对象:文件,资源,内存中的对象,或者是一些难以复制的东西. 正文 我们

C#设计模式(13)——代理模式(Proxy Pattern)

原文:C#设计模式(13)--代理模式(Proxy Pattern) 一.引言 在软件开发过程中,有些对象有时候会由于网络或其他的障碍,以至于不能够或者不能直接访问到这些对象,如果直接访问对象给系统带来不必要的复杂性,这时候可以在客户端和目标对象之间增加一层中间层,让代理对象代替目标对象,然后客户端只需要访问代理对象,由代理对象去帮我们去请求目标对象并返回结果给客户端,这样的一个解决思路就是今天要介绍的代理模式. 二.代理模式的详细介绍 代理模式按照使用目的可以分为以下几种: 远程(Remote

乐在其中设计模式(C#) - 代理模式(Proxy Pattern)

原文:乐在其中设计模式(C#) - 代理模式(Proxy Pattern)[索引页][源码下载] 乐在其中设计模式(C#) - 代理模式(Proxy Pattern) 作者:webabcd 介绍 为其他对象提供一个代理以控制对这个对象的访问. 示例 有一个Message实体类,某对象对它的操作有Insert()和Get()方法,用一个代理来控制对这个对象的访问. MessageModel using System;using System.Collections.Generic;using Sy

设计模式之七(代理模式)

原文:设计模式之七(代理模式) 前言 代理模式:为其他对象提供一种代理以控制对这个对象的访问. 结构图   Subject类,定义了RealSubject和Proxy的共用接口,这样就在任何使用RealSubject的地方都可以使用Proxy. RealSubject类,定义Proxy所代表的真实实体 Proxy类,保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以来替代实体. 实例代码  Subject类 public abstract class S

【设计模式】—— 代理模式Proxy

模式意图 代理模式为其他的对象增加一个代理对象,进行访问控制.从而避免直接访问一个对象,造成效率或者安全性上的降低. 应用场景 1 远程代理,为一个远程对象,创建一个本地的代理对象.每次访问,直接访问本地代理对象即可. 2 虚代理,如果对象很大,直接访问开销很大,可以为他创建一个代理对象,只生成关键的信息即可. 3 保护代理,为某个对象增加一种保护机制,只有一定的权限才能通过这个代理,访问后面的对象. 4 智能指针,有点像C++里面的那个智能指针,为指针进行计数和销毁等操作.避免出现悬垂指针等现