为了账号安全,请及时绑定邮箱和手机立即绑定

模式的秘密---代理模式

难度中级
时长 2小时 0分
学习人数
综合评分9.70
184人评价 查看评价
9.8 内容实用
9.6 简洁易懂
9.7 逻辑清晰
  • 动态代理实现步骤:

    1. 创建实现InvocationHandler的类,必须实现invoke方法

    2. 创建被代理的类以及接口

    3. 调用Proxy的静态方法,创建一个代理类:newProxyInstance

    4. 通过代理调用方法

    查看全部
  • 被代理的类及其方法
    查看全部
  • 测试,创建CglibProxy()代理类型的实例,调用代理方法,传入被代理的 .class 类 ;方法中根据该类型执行相关的逻辑执行Train类的实例化;最后由代理的实例化对象调用move()方法
    查看全部
  • 代理类的拦截代理方, intercept 中根据获得的代理类,有代理类实现目标类的Method

    查看全部
  • 创建getProxy(Class clazz) 获取代理类,传入的参数是要被代理的类的引用
    查看全部
  • cglibproxy代理: 实现MehodInterceptor 方法拦截接口,并实现intercept(参数1,餐数2,参数3)方法

    查看全部
  • 产生代理类的方法当中业务逻辑是固定的,如何动态的实现指定的业务逻辑?

    答:可以创建事物处理器,这个事物处理器可以专门对某个对象的方法来进行处理。

    案例:

    创建一个接口,该接口的名称和JDK的InvocationHandler接口(事物处理器)保持一致,该接口中也定义一个invoke方法,该方法的作用是对某个对象的方法进行处理,所以该方法中需要传入该方法的对象,以及方法,这里为了简便起见,就不传递方法参数了,如图。

    https://img3.sycdn.imooc.com/5d482dcb0001bd9007000214.jpg

    实现具体的事物处理:

    创建一个类并实现InvocationHandler接口,名称为TimeHandler,实现接口的方法中,在该方法中实现传递进来对象的方法。(注意:这里传递的是代理的对象,我们需要传递被代理的对象,并且调用被代理对象的方法,所以创建一个私有属性Object类型的target,并通过构造方法为该属性赋值)这时调用的才是被代理对象的方法。

    https://img2.sycdn.imooc.com/5d482f130001073c06880493.jpg

    在调用该方法的前后可以增加相应的业务逻辑。如图。

    https://img3.sycdn.imooc.com/5d4830dc0001929106680437.jpg

    这样事物处理就完成了。这里需要在newProxyInstance(Class infce,InvocationHandler h)方法中增加该接口的参数。

    https://img1.sycdn.imooc.com/5d4831950001a0f712700189.jpg

    InvocationHandler参数传进来之后,需要在动态产生动态代理类源码中

    ,把InvocationHandler传进来,并把该接口引入进来,如图。

    https://img2.sycdn.imooc.com/5d4832580001503f08750381.jpg

    这里"private"+infce.getName()+"m;"+rt+就使用不到了就可以删除掉了。

    代理类$Proxy0的构造方法的参数传进来的参数是InvocationHandler,

    https://img1.sycdn.imooc.com//5d496ff50001136610130365.jpg接下来需要在代理类中调用InvocationHandler接口的invoke方法,该方法中有两个参数,一个是代理对象,一个是代理对象的方法,代理对象就是当前的对象this,方法就是当前的方法("Method md="+infce.getName()+".getClass().getMethod(\""+m.getName()+"\");。

    https://img1.sycdn.imooc.com//5d4971700001bbea08600368.jpg完整代码:

    https://img1.sycdn.imooc.com//5d49734300016b3612940717.jpghttps://img1.sycdn.imooc.com//5d49734b0001465913300655.jpg测试:

    https://img1.sycdn.imooc.com//5d4976600001f54611180264.jpg


    查看全部
  • 此时已经生成java文件,还需要进行动态的编译生成的代理类。

    首先需要获取当前系统的java编译器:

    【a】JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();

    创建文件的管理者:

    【b】StandardJavaFileManager fileMgr=compiler.getStandardFileManager(diagnosticListener,locale,charset),该方法的第一个参数是扫描监听器,第二个和第三个都是国际化参数,这里为了简单起见都设置为null。

    https://img1.sycdn.imooc.com//5d4822e20001cd0607720358.jpg


    根据文件的路径来得到管理文件的数组:

    【c】Iterable units=fileMgr.getJavaFileObjects(filename);

    获取编译的任务:

    【d】CompilationTask t=complier.getTask(null,fileMgr,null,null,null,units),第一个参数是输出的位置,这里使用null。第二个参数是文件管理者。第三个参数是监听器。第四个和第五个参数是国际化参数这里都设置为null。第六个参数是要编译的文件,该方法返回的是编译的任务。

    https://img1.sycdn.imooc.com//5d4822c50001480108360261.jpg

    接下来进行编译:

    【5】t.call();

    最后把文件管理者关闭掉:

    【6】eMgr.close();

    https://img1.sycdn.imooc.com//5d4823480001032108400228.jpg

    这样就可以对生成的$Proxy0进行编译(产生$Proxy0.class,说明编译成功,如图)。

    https://img1.sycdn.imooc.com//5d4824c20001e6b903720166.jpg

    【3】将这个类加载到内存当中,产生一个新的对象(该对象就是代理对象)。

    因为生成的文件是在指定的目录下,可以通过ClassLoader进行加载:

    【a】获取类加载器:ClassLoader cl=ClassLoader.getSystemClassLoader();

    通过类加载器加载文件:

    【b】Class c=cl.loadClass("com.imooc.proxy.$Proxy0");参数是相对路径。这里为了验证可以通过c.getName()把加载的类名输出。

    https://img1.sycdn.imooc.com//5d48279000011e0d06690201.jpg

    这样就会把代理类加载到内存当中。

    【4】return该代理对象。

    产生代理对象并进行返回,创建代理类可以通过构造器进行初始化,根据构造器方法传递的参数来创建该对象(构造器中传递的是接口实现类的对象):

    Constructor ctr=c.getConstructor(infce);

    根据构造器创建代理对象:

    ctr.newInstance(new Car());

    https://img1.sycdn.imooc.com//5d4828af000181f707080235.jpg

    这样就创建了动态代理类。

    测试:


    https://img1.sycdn.imooc.com//5d4828ec0001a2d008960385.jpg



    查看全部
  • 代理
    查看全部
  • JDK动态代理概念:在代理类和被代理类之间增加了InvocationHandler接口和Proxy类来动态的产生代理,通过上节案例了解JDK动态代理可以对实现某些接口的任意类的任意方法产生任意的代理。

    通过案例模拟JDK动态代理的内部实现:

    实现Proxy.newProxyInstance(被代理类的类加载器,被代理类实现的接口,事务处理器对象)的功能————思路:创建一个类,提供一个静态方法,该类的作用和Proxy类的作用相同 (这里关键是如何通过该方法动态产生代理,以支持实现了某些接口的任意类任意方法产生代理)。

    动态代理实现思路:

    【a】声明一段源码:该源码就是在Proxy类中定义一个字符串,该字符串的值就是静态代理TimeProxy类的源码(这里在它的基础上进行改造)。

    手动创建Proxy类,并提供newInstance的静态方法(模拟JDK动态代理的Proxy调用静态方法newInstance()),并在该方法中声明一个字符串该字符串的值是静态代理类TimeProxy(源码)的所有,如下图(Windows下的换行符:"\r\n"。)。

    这里给代理类的文件名换成JDK生成动态代理的文件名$Proxy0。

    注意:由于源码中已经有引号了,所以可以通过转义引号\"\"来代替引号。


    https://img1.sycdn.imooc.com//5d42df250001891413260833.jpg


    https://img1.sycdn.imooc.com//5d42dfcd0001793807450183.jpg

    通过JDK动态产生代理类获取名字方式:因为该方式返回的就是代理类,代理对象.getClass.getName(),结果可以看到JDK产生动态代理的名字就是$Proxy0。

    https://img1.sycdn.imooc.com//5d42e0420001187a09170174.jpghttps://img1.sycdn.imooc.com//5d42e08c00017e8f09860282.jpg

    【b】编译源码(编译时使用JDK Compiler API),编译成功后就会产生新的类(该类就是代理类)

    编译的方式:一般先生成一个java文件,然后对该java文件进行编译。

    首先定义文件的路径,文件路径的值一般取当前应用所在的路径(当前路径获取方式:System.getProperty("user.dir"),这样方便进行编译。如下图,测试输出。也可以把该项目放在指定目录下,在后面拼接指定路径加类名。

    https://img1.sycdn.imooc.com//5d42e2ff00013a8207320131.jpghttps://img1.sycdn.imooc.com//5d42e34b0001652908480280.jpghttps://img1.sycdn.imooc.com//5d42e3f50001679410340100.jpg

    根据这个路径生成一个java文件。

    https://img1.sycdn.imooc.com//5d42e4760001fd0410370130.jpg

    接下来需要把该源码生成到java文件当中,通过commons-io包下的FileUtils类可以快速的对文件进行读写删除等等操作,这里使用writeStringToFile(File对象,Stirng对象)(jre1.6不支持编译,这里改成jdk)

    https://img1.sycdn.imooc.com//5d42e5820001c9fc09830216.jpg

    这样在运行测试程序时,就会生成一个$Proxy0.java文件放到指定目录下,可以在Navigator视图下的bin目录下查看到。

    https://img1.sycdn.imooc.com//5d42e73f00014eca10560267.jpg

    因为实现的目标是想对任意对象任意方法产生任意的代理,所以该方法还需要改造,也就是源码中代理类

    https://img1.sycdn.imooc.com//5d42ea660001719b10430525.jpghttps://img1.sycdn.imooc.com//5d42ea9d0001fabe08600165.jpg

    由于是要动态产生代理,所以静态代理源码中实现接口的方法也需要通过传进来的参数来获取。所以定义一个String用来获取方法。

    https://img1.sycdn.imooc.com//5d42edd70001d6e409630468.jpg

    还需要把方法替换成methodStr,如图

    https://img1.sycdn.imooc.com//5d42ee250001602e07050186.jpg

    测试:

    https://img1.sycdn.imooc.com//5d42ee7c000119b608610189.jpg



    查看全部
  • 使用cglib动态产生代理

    JDK动态代理机制:只能代理实现某些接口的类,如果没有实现接口的类则不能使用JDK动态代理。

    cglib动态代理机制:针对类产生代理,原理就是为指定的目标类产生一个子类,子类通过方法拦截技术(覆盖父类的方法来实现功能的增强)拦截所有父类方法的调用(因为该种方式是使用继承方式,所以不能对final修饰的类进行代理)。

    CGLIB产生代理案例:

    步骤1:导入cglib的jar包。

    步骤2:创建一个Train类,它并没有实现某个接口,并提供给它一个move方法。

    步骤3:创建一个CglibProxy类来产生代理,它需要实现接口MethodInterceptor,并实现了intercept方法,该方法作用就是拦截所有目标类方法的调用(第一个参数:目标类的实例。第二个参数:目标方法的反射对象。第三个参数:目标方法的参数。第四个参数:Proxy代理类的实例)。

    https://img1.sycdn.imooc.com//5d41849e00011ffa07670434.jpg

    调用被代理类的方法:在intercept方法中,通过代理类实例的invokeSuper(obj,m)方法调用父类的方法,第一个参数就是目标类的对象。第二个参数是目标类方法的参数。(因为cglib是使用继承的方式实现动态代理,所以产生的代理类是被代理类的子类)

    该方法需要返回该代理

    https://img1.sycdn.imooc.com//5d4185d20001ba8a07980469.jpg

    步骤5:由于调用intercept方法中需要传入代理类实例,所以在该类中需要提供得到代理类实例的方法,创建代理类需要使用Enhancer属性,而且通过Enhancer创建代理类还需要传入被代理类的类类型,enhancer.setSuperclass(被代理类的类类型),表示要创建哪一个类的代理,在创建代理实例之前,还需要调用回调函数enhancer.setCallback(this),最后返回代理实例enhancer.create();

    https://img1.sycdn.imooc.com//5d4188930001ff6707750367.jpg

    测试:首先需要创建拥有返回代理类实例方法的对象,通过这个对象调用返回代理实例的方法,然后赋值给该类,再调用方法。

    https://img1.sycdn.imooc.com//5d41893800012fc106000238.jpg

    查看全部
  • 问题:这里是Car需要代理来实现时间和日志的处理,如果有一个火车,那么还需要增加一个火车时间处理的代理类。不同的类实现这个代理能不能组合成一个代理呢?

    动态代理概念:动态产生代理,实现对不同类,不同方法的代理。

    JDK动态代理实现方式:在ProxySubject代理类和RealSubject被代理类之间增加了一个实现了InvocationHandler的一个ProxyHandler类(事物处理器),日志处理和时间处理都是在事物处理中完成的。

    https://img1.sycdn.imooc.com//5d3fe5480001d29d09500413.jpg

    其中Java动态代理类(InvocationHandler)位于位于java.lang.reflect包下,一般主要涉及到以下两个类:

    (1)Interface InvocationHandler:该接口中仅定义了一个方法public object invoke(Object obj,Method method,Object[] args)在实际使用时,第一个参数表示被代理的对象,第二个参数代表被代理的方法,第三个参数代表被代理方法的参数,args是一个数组。这个抽象方法在代理类中动态实现。

    https://img1.sycdn.imooc.com//5d3fe554000164f710630519.jpg

    (2)Proxy:产生动态代理的类,通过static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)就可以动态返回代理类的一个实例,返回后的代理类可以当做被代理类使用(可使用被代理类的在接口中声明过的方法)。第一个参数是被代理类的类加载器。第二个参数是被代理类实现了哪些接口。第三个参数是事件处理器。

    https://img1.sycdn.imooc.com//5d3fe6090001406010960511.jpg

    代码实现:

    【步骤1】由于要使用Proxy类动态生成代理的方法中第三个参数需要传入事物处理器的对象。首先需要创建事物处理器。如下图,由于invoke方法中使用到被代理对象,所以构造方法中传入被代理对象的实例,invoke方法中就是为了实现代理功能,通过method.invoke(被代理的对象),这样被代理对象的方法就会执行了。

    https://img1.sycdn.imooc.com//5d4038e90001756c12030445.jpg

    【步骤2】通过产生动态代理的Proxy类的newProxyInstance方法可以产生代理,第一个参数为被代理对象的类加载器,第二个参数为被代理对象实现的接口,第三个参数为事物处理器。

    https://img1.sycdn.imooc.com//5d403a280001424012950282.jpg

    总结:

    JDK动态代理是被代理类首先需要实现某些接口,这个代理在运行时产生代理对象,但该代理并不能做任何操作,操作是通过实现InvocationHandler接口的事物处理器实现的。

    https://img1.sycdn.imooc.com//5d403c5e0001591e09510574.jpg



    查看全部
  • 聚合比继承更适合代理模式

    案例描述:上节课程中,对Car类的move方法增加了时间处理的额外功能,现在想实现代理之间功能的叠加,也就是在move方法前后不仅做时间的处理,还做权限的管理和增加日志的管理。

    通过继承实现上述功能:Car1的move方法里调用Car的move方法前后增加了时间处理的功能,现在要做代理类功能的叠加(先记录日志再记录汽车行驶的时间)。首先需要创建一个Car3,来继承Car1(Car1通过继承方式实现静态代理),或者继承Car,然后在move方法前后先实现日志,再实现汽车行驶时间的处理(这样会先输出日志再输出时间处理)。

    如果先要记录汽车行驶的时间,再进行日志的处理,则需要创建一个Car4,先对时间处理,再对日志处理。

    如果先要权限的判断、再进行日志的处理、然后再进行时间处理,则需要创建一个Car5。

    如果先要权限的判断、再进行时间的处理、然后再进行日志的处理,则需要创建一个Car6.

    结果得出如果使用继承的方式实现代理功能的叠加,代理类会无限的增多,也就是用一个代理则需要创建一个代理,所以继承的方式实现代理功能的叠加不是推荐的。

    通过聚合实现上述功能:由于代理类和被代理类都实现了相同的接口 ,所以代理类的构造方法中也可以传入接口的对象(针对Car2代理类),代理类中声明的也是接口的对象。再创建一个CarLogProxy代理类(它实现了接口)用来做日志的功能。

    如下是先记录日志再记录时间

    https://img2.sycdn.imooc.com/5d3ee8cc00016d2207090190.jpg

    https://img2.sycdn.imooc.com/5d3ef4c90001823c04830229.jpg

    如下是先记录时间再记录日志

    https://img1.sycdn.imooc.com/5d3ee90a00016a7906060229.jpg

    https://img3.sycdn.imooc.com/5d3ef4ac0001702d05830242.jpg

    结果通过聚合方式实现代理会比继承方式灵活很多,使用聚合方式代理之间是可以相互传递的,所以推荐使用聚合方式实现代理模式(这里通过聚合实现代理实现了不同功能的顺序)。


    查看全部
  • 以智能引用代理为例:

    代理的两种实现方式

    案例描述:提供一个接口,该接口中提供一个车的move方法。正常情况下没有使用代理时,创建一个Car实现该接口,并实现该接口的方法,然后就可以通过创建该实现类的对象并赋值给接口来调用该方法了。

    【1】静态代理:代理和被代理对象在代理之前是确定的,他们都实现相同的接口或者继承相同的抽象类。

    【a】继承的方式实现静态代理:正常情况下是通过Car的方法来完成操作,代理的意思是,创建一个类Car1并继承类Car,并在继承类Car1的构造方法中通过super调用父类的方法,也可以在方法中添加一些额外的功能,这个类Car1就是类Car的代理,最后就可以通过创建Car1的对象赋值给接口,再调用move方法。

    【b】聚合的方式实现静态代理:聚合就是在该类中声明被代理的对象,创建一个Car2,并实现该接口,然后在类中声明Car,通过构造方法对Car的对象进行赋值,然后在实现的方法中通过Car对象调用move()方法。

    【2】动态代理


    查看全部
  • 代理模式概念:为其他对象提供一种代理以控制对这个对象的访问,代理对象起到中介作用。

    常见的几种代理模式:

    1、远程代理:代理模式的经典应用,类似于客户端、服务器的模式,为不同地理的对象提供局域网代表对象。

    2、保护代理:控制对一个对象的访问权限。

    3、智能代理:提供目标对象额外的一些服务。

    4、虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。

    查看全部

举报

0/150
提交
取消
课程须知
本课程是 Java 开发课程的高级课程,希望各位小伙伴们在学习本课程之前,了解以下内容: 1)初步具备面向对象的设计思维 2)了解多态概念 3)了解反射
老师告诉你能学到什么?
1、代理模式概念机分类 2、代理模式应用场景 3、掌握静态代理、动态代理运用 4、理解JDK动态代理实现原理

微信扫码,参与3人拼团

意见反馈 帮助中心 APP下载
官方微信
友情提示:

您好,此课程属于迁移课程,您已购买该课程,无需重复购买,感谢您对慕课网的支持!