在我们通常的应用中,代理模式也是我们常用的设计模式之一。所谓的代理模式是指客户端并不直接调用实际的对象,而是通过调用代理对象,来间接的调用实际的对象。

为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。

在我们现实生活中,这种情形也是非常常见非常常见的,就比如,黄牛买票,黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。

在代码实现中,相当于为一个委托对象Station(车站)提供一个代理对象Scalper(黄牛),通过Scalper(黄牛)可以调用Station(车站)的部分功能,并添加一些额外的业务处理,同时可以屏蔽Station(车站)中未开放的接口。

代理模式的UML图

代理模式类图

1,Station(车站)是委托类,Scalper(黄牛)是代理类;

2,Subject是委托类和代理类的接口;

3,sell()是委托类和代理类的共同方法;

从UML图中,可以看出代理类与真正实现的类都是实现了抽象的接口,这样的好处的在于代理类可以与实际的类有相同的方法,可以保证客户端使用的透明性。

二 Java常用的三种代理

2.1 静态代理

在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽realSubject中未开放的接口。

1、RealSubject 是委托类,Proxy 是代理类;
2、Subject 是委托类和代理类的接口;
3、request() 是委托类和代理类的共同方法;

具体代码实现如下:

interface Subject {    void request();}class RealSubject implements Subject {    public void request(){        System.out.println("RealSubject");    }}class Proxy implements Subject {    private Subject subject;    public Proxy(Subject subject){        this.subject = subject;    }    public void request(){        System.out.println("begin");        subject.request();        System.out.println("end");    }}public class ProxyTest {    public static void main(String args[]) {        RealSubject subject = new RealSubject();        Proxy p = new Proxy(subject);        p.request();    }}

静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。

2.2 动态代理

动态代理有以下特点:

  1. 在运行期,通过反射机制创建一个实现了一组给定接口的新类;

  2. 在运行时生成的class,必须提供一组interface给它,然后该class就宣称它实现了这些 interface。该class的实例可以当作这些interface中的任何一个来用。但是这个Dynamic Proxy其实就是一个Proxy, 它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

  3. 动态代理也叫做:JDK代理,接口代理

  4. 接口中声明的所有方法都被转移到调用处理器(handler)一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强。

JDK中生成代理对象的API
  代理类所在包:java.lang.reflect.Proxy
  JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

    static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler handler)

  注意该方法是在Proxy类中的静态方法,且接收的三个参数依次为:

  • ClassLoader loader:指定当前目标对象使用的类加载器,用null表示默认类加载器

  • Class [] interfaces:需要实现的接口数组

  • InvocationHandler handler:调用处理器,执行目标对象的方法时,会触发调用处理器的方法,从而把当前执行目标对象的方法作为参数传入

java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。

    // 该方法负责集中处理动态代理类上的所有方法调用。第一个参数是代理类实例,第二个参数是被调用的方法对象,第三个参数是方法参数的数组形式    // 第三个方法是调用参数。    Object invoke(Object proxy, Method method, Object[] args)

代码示例:

package model;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;interface IUserDao {    void save();}class UserDao implements IUserDao {    public void save() {        System.out.println("----已经保存数据!----");    }}class ProxyFactory {    private Object target;    public ProxyFactory(Object target) {        this.target = target;    }    // 给目标对象生成代理对象,其class文件是由 JVM 在运行时动态生成    public Object getProxyInstance() {        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),                new InvocationHandler() {                    @Override                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                        System.out.println("开始");                        // 执行目标对象方法,方法参数是target,表示该方法从属于target                        Object returnValue = method.invoke(target, args);                        System.out.println("提交");                        return returnValue;                    }                });    }}public class Client {    public static void main(String[] args) {        // 目标对象        IUserDao target = new UserDao();        System.out.println(target.getClass());        // 代理对象        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();        System.out.println(proxy.getClass());        proxy.save();    }}

输出:

class model.UserDaoclass model.$Proxy0开始----已经保存数据!----提交

总结:
  代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

2.3 Cglib代理

  上面的静态代理和动态代理模式都是要求目标对象实现一个接口或者多个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用构建目标对象子类的方式实现代理,这种方法就叫做:Cglib代理。
  Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)

  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的子类

  • 代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会被拦截。

代码示例:
目标对象类:UserDao.java

    /**     * 目标对象,没有实现任何接口     */    public class UserDao {        public void save() {            System.out.println("----已经保存数据!----");        }    }

Cglib代理工厂:ProxyFactory.java

    /**     * Cglib子类代理工厂    * 对UserDao在内存中动态构建一个子类对象    */    public class ProxyFactory implements MethodInterceptor{         //维护目标对象        private Object target;        public ProxyFactory(Object target) {            this.target = target;        }       //给目标对象创建一个代理对象        public Object getProxyInstance(){            //1.工具类            Enhancer en = new Enhancer();           //2.设置父类            en.setSuperclass(target.getClass());            //3.设置回调函数            en.setCallback(this);            //4.创建子类(代理对象)            return en.create();        }        @Override        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {            System.out.println("开始事务...");            //执行目标对象的方法            Object returnValue = method.invoke(target, args);            System.out.println("提交事务...");            return returnValue;        }    }

测试类:

    /**     * 测试类     */    public class App {        @Test        public void test(){            //目标对象            UserDao target = new UserDao();            //代理对象            UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();            //执行代理对象的方法            proxy.save();        }    }

java模拟火车站买票的过程_Java常用代理相关推荐

  1. java模拟多线程买票问题

    多线程买票是java中的一个经典案例,其主要思想无非包括2点,synchronized和锁,两者中,前者实现同步,后者为同步的线程提供锁,从而实现多个线程共享同一份资源时候,能够同步进行; 经典的方式 ...

  2. Java知识点26——模拟12306买票过程、模拟龟兔赛跑的过程、静态代理例子

    模拟12306买票过程 共享资源,并发(线程安全) Web12306.java /*** 共享资源,并发(线程安全)* @author Administrator**/ public class We ...

  3. 用java模拟dnf武器强化的过程

    用java模拟dnf武器强化的过程 几点说明: 需要用户输入武器初始等级以及是否需要进行强化. 整个强化过程都包含在一个while循环里,利用标记i来停止循环,i==1继续强化:i==0为停止强化. ...

  4. Java之多线程买票程序

    Java之多线程买票程序 1.要求 要求五个线程,分别命名为售票窗口1.售票窗口 2.......售票窗口5, 一共100张票,每个售票窗口卖票的数量大致相同(20)张卖票时给与编号,每张票唯一. 每 ...

  5. java 模拟火车站售票系统_模拟售票系统java编程

    模拟售票系统java编程 /* 项目:用多线程设计一个模拟火车站售票大厅的工作情形. 问题描述:火车站有许多售票窗口,有些开放,有些不开放.顾客进入火车站售票厅后,到某个售票窗口排队等候,排到了就办理 ...

  6. 使用多线程模拟电影院买票。

    模拟唐僧师徒4人买电影票.电影票10元一张,唐僧拿着一张50元的.孙悟空那和 猪八戒都拿着一张20的. 沙僧拿着一张10元的.此时售票员手中只有一个10元的. 模拟他们4个人买票. package c ...

  7. Python 每日一记210java模拟电影院买票

    package mypackage;/***电影院买票系统* 注意泛型和容器的使用,特别注意容器内一次性添加多个元素的方法Arrays.asList(1,2,3,4,5)* 注意线程同步问题的解决,特 ...

  8. 周五上海火车站买票游记 绝对真实

    玛丽隔壁 时间:上个星期五(一月25号)晚7:00--周六(一月26早)7:00 地点:上海火车站 天气:雨..半夜雨夹雪 人物:我.. 回家地点:安徽安庆--黄梅戏发源地 旅游景点天柱山所在地 周五 ...

  9. java多线程举例买票之买电影票出现的了相同的票和负数票的原因分析和解决方法

    package cn.itcast_03; /** 我们在前面讲解的电影院售票程序,从表面上看,没什么问题* 但是在,真实的生活中,售票时网络时不能实时传输* 会有延迟的* * 通过加入延迟,就产生了 ...

最新文章

  1. mysql添加用户查重的方法_mysql 开发技巧之JOIN 更新和数据查重/去重
  2. JAVAC 命令详解
  3. Swift の 函数式编程
  4. thinkpaidE480office安装文件夹
  5. Mooc的Python3学习笔记
  6. jQuery之.queue()
  7. BUS HOUND调试USB驱动遇到的错误代码解析
  8. HDU - 4780费用流
  9. 城市智慧灯杆解决方案
  10. SpringBoot数据访问CannotGetJdbcConnectionException: Failed to obtain JDBC Connection异常的解决方式。
  11. 吴恩达deeplearning之CNN—人脸识别与风格化转换(2)
  12. wps excel 表格给一列数据添加相同的内容的方法
  13. 腾讯内部深度文章曝光:微信向左 手机QQ向右
  14. Win10下配置PHP环境变量
  15. python自动化测试实战-无涯(学习与研究)[一]
  16. The King’s Ups and Downs HDU - 4489(计数+dp)
  17. 如何选择适合你的兴趣爱好(十五),油画
  18. 智能电视硬件架构设计——整机研发
  19. 求解n个二进制数之间的最小码距
  20. 小龙虾,这个“三流入侵者”竟成“钻石网红”?

热门文章

  1. [开源] FreeSql AOP 功能模块 - FreeSql
  2. .Net Core 2.1 通用主机(Core 在控制台应用程序中的应用)
  3. 微软将人工智能嵌入Windows 10更新
  4. C#热度不如Java?网友呛声:还有使用C#不能完成的工作?
  5. 图像识别:微信跳一跳机器人
  6. MySql 使用 EF Core 2.0 CodeFirst、DbFirst、数据库迁移(Migration)介绍及示例
  7. .NET Core 2.0 开源Office组件 NPOI
  8. 拆分:分解单块系统——《微服务设计》读书笔记
  9. 服务的协作:服务间的消息传递——《微服务设计》读书笔记
  10. 你还可以续命几次?回顾DockPanel Suite项目的发展史