软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 架构设计 -> Java设计模式(八) -> 正文阅读

[架构设计]Java设计模式(八)


代理模式
1.生活中:
代理就是一个人或者一个组织代表其他人去做一件事的现实生活中的。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
2.官方:
代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用
一、静态代理
类图结构如下
[img]http://img.blog.csdn.net/20160328210954511
在代理模式中的角色:
  ●抽象主题角色:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
  ●真实主题角色:定义了代理对象所代表的目标对象。
  ●代理主题角色:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。它可以增加一些真实主题里面没有的功能。
生活中的例子:过年加班比较忙,没空去买火车票,这时可以打个电话到附近的票务中心,叫他们帮你买张回家的火车票,当然这会附加额外的劳务费。但要清楚票务中心自己并不卖票,只有火车站才真正卖票,票务中心卖给你的票其实是通过火车站实现的。这点很重要!
上面这个例子,你就是“客户”,票务中心就是“代理角色”,火车站是“真实角色”,卖票称为“抽象角色”!
代码
抽象主题角色
//抽象角色:声明真实对象和代理对象的共同接口;
public interface  TicketManager { 
    /**
     * 售票
     */
    public  void  soldTicket();
    /**
     * 改签
     */
    public void changeTicket();
    /**
     * 退票
     */
    public void returnTicket();
}   

真实主题角色
public class TicketManagerImpl implements TicketManager {

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

    @Override
    public void changeTicket(){
        //checkIdentity();
        System.out.println("改签");
    }

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

    /**
     * 身份验证
     */
    public void checkIdentity(){
        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()方法在调用被代理类的真实方法的前后都可进行一定的特殊 操作。这是动态代理最明显的优点
类图
[img]http://img.blog.csdn.net/20160328211321804
代码
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();
    }
}

结果同上
......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2016-03-29 23:00:06  
架构设计 最新文章
Opengl教程之读取obj并绘制在picturecontro
读《企业应用架构模式》第五章并发
StepbyStepintoSpring(IOC)
设计模式(2)用例图之一
使用实体组件系统(ECS)开发”吃方块”游戏实
编程学习之简单工厂模式与策略模式
Invalidprojectdescription.
基于Redis实现分布式消息队列(2)
《开源框架那点事儿15》:借船下海还是造船
原型模式——浅复制和深复制
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年1日历
2018-1-16 21:14:24
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --