引言:Java 动态代理机制的出现,使得开发人员不用手工编写代理类,只要指定一组接口及委托类的对象,便能动态的获得代理类。代理类会负责将所有的方法调用分配到委托对象上反射执行,在分配指定过程中,开发人员还可以按需掉正委托类对象及其功能,是一套非常灵活有弹性的代理框架。
一、什么是代理模式
为其他对象提供一种代理以控制对这个对象的访问。代理类负责为委托类预处理消息,过滤信息并转发消息,以及进行消息被委托执行后的后续处理。
为了保持行为的一致性,委托类和代理类会实现相同的接口,所以,在访问者角度来看二者并无区别。通过代理类这中间一层,能有效控制对委托类的直接访问,也能很好的隐藏和保护委托类的对象,同时也为实施不同控制策略预留了空间,从而在设计上具有更大的灵活。
二、代理模式如何使用
1、在进行下一步之前,我们先来讨论一下 继承、聚合 孰优孰劣。
1.1 接口
1.2 委托类
1.3 继承委托类实现代码运行时间的计算
1.4 代理类
1.5 运行
当我们想去增加一个记录日志的功能时,通过继承,需要再写一个方法来继承超类。如果想要一个功能为先记录日志,后计算代码运行时间,或二者颠倒或再加入新的功能,我们会发现通过继承来实现的方式会变得十分庞大而且不利于维护。所以继承是最不推荐的方式,那么采用聚合的方式就会更好吗?举例如下:
代理类:记录时间
代理类:记录日志
运行
运行结果:
由此可以看出,通过聚合来实现的方式有很大的优势,而且使得代码的可扩展性也大大提高。
三、动态代理
动态代理类的字节码在程序运行时,通过 Java 的反射机制动态生成,无需手工编写源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为 Java 的反射机制可以生成任意类型的动态代理类。 Java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口提供了动态生成代理类的能力。
代码如下:(动态代理机制)
1、定义接口 Moveable
2、定义委托类
3、模拟 Proxy 类(因为往后还有更深的改动,于是将方法名定义为:XXX1)
Proxy 类的主要功能:
-- 代理类生成的目的地
-- 编译生成 .class 文件
-- 加载到内存,生成新的对象
-- 产生新的构造方法
------------------------------------------------------------------------------------
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class Proxy {
public static Object newProxyInstance1() throws Exception{
String rt = "\r\n";
String str =
"package com.inspur.simpleProxy;" + rt +
"public class TankTimeProxy implements Moveable{" + rt +
" public TankTimeProxy(Moveable t){" + rt +
" this.t = t;" + rt +
" }" + rt +
" Moveable t;" + rt +
" @Override " + rt +
" public void move()" + "{" + rt +
" System.out.println(\"Start time\");" + rt +
" long start = System.currentTimeMillis();" + rt +
" t.move();" + rt +
" long end = System.currentTimeMillis();" + rt +
" System.out.println(\"using time == \" + (end - start));" + rt +
" System.out.println(\"End time\"); " + rt +
" }" + rt +
"}";
/*
* 要生成的目的地
*/
String fileName = System.getProperty("user.dir") + "/src/com/inspur/simpleProxy/TankTimeProxy.java";
File file = new File(fileName);
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(str);
fileWriter.flush();
fileWriter.close();
/*
* 编译,生成 .class 文件
*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
System.out.println(compiler.getClass().getName());
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable iterable = fileManager.getJavaFileObjects(fileName);
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, iterable);
task.call();
fileManager.close();
/*
* load to memory , create an instance
* 加载到内存,生成新对象
*/
// 将硬盘上的 Java 文件,放到内存中的类中,需要指定路径
// 此方式还可以从网上去 load 类,通过网络传来的类 , 也可以通过此方式来 load 类
// user.dir 当前文件根目录
URL[] urls = new URL[]{new URL("file:/" + System.getProperty("user.dir") + "/src")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class c = urlClassLoader.loadClass("com.inspur.simpleProxy.TankTimeProxy");
System.out.println(c);
// 拿到其中的构造方法,得到方法,通过参数来确定。 找一个构造方法,这个构造方法的参数类型是 Moveable ,便得到了 TankTimeProxy
Constructor constructor = c.getConstructor(Moveable.class);
//Object m = constructor.newInstance(new Tank());
//m.move();
// c.newInstance(); 会产生一个新的构造方法
Moveable m = (Moveable) constructor.newInstance(new Tank());
return m;
}
------------------------------------------------------------------------------------
4、编写 Client 类来对 Proxy 的方法进行调用。
5、运行结果如下
-- 通过此例,我们可以发现,TankTimeProxy 类是自动生成的,意味着我们根本不需要知道代理类是什么,只需要去调用 Proxy.newProxyIm=nstance 即可,这样就大大减少了很多具体代理类的生成,这就是动态代理,.class 文件是我们动态生成的。
这样做确实可以产生一个动态代理,但是,我们会发现,我们所生成的代理,只能代理 moveable 接口。那么,如何产生任意接口的呢?,请看
设计模式 --> 代理模式(二),将会有各种改进。
四、代理模式的作用
1、远程代理:为一个对象在不同地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。
2、虚拟代理:根据需要创建开销很大的的对象,通过它来存放实例化需要很长的时间真实对象。
3、安全代理:用来控制真实对象访问时的权限。
4、智能代理:当调用真实对象时,代理处理另外一些事。