代理模式
使用率非常高,为其它对象提供一种代理,可以控制对对象的访问,也称委托模式,是一项基本设计技巧,代理模式可以提供很好的访问控制。
代理模式结构
Subject(抽象主题角色):可以是抽象类也可以是接口,无特殊要求。
RealSubject(具体主题角色):也成为被代理角色、被委托角色,业务逻辑具体执行者。
Proxy(代理角色):代理类、委托类,负责对真实角色的应用,把所有抽象主题角色定义的方法交给具体主题角色实现,并提供预处理和善后处理工作。
/**
* 抽象主题角色
* @author 张家骏
*
*/
public interface Subject {
public void request();
}
/**
* 具体主题角色
* @author 张家骏
*
*/
public class RealSubject implements Subject{
@Override
public void request() {
System.out.println("RealSubject request");
}
}
/**
* 代理主题角色
* @author 张家骏
*
*/
public class Proxy implements Subject{
private Subject subject = null;
public Proxy(Subject subject){
this.subject = subject;
}
public Proxy(Object... objs) {
}
@Override
public void request() {
this.before();
this.subject.request();
this.after();
}
/**
* 预处理
*/
private void before() {
System.out.println("do something");
}
/**
* 后处理
*/
private void after() {
System.out.println("do something");
}
}
要代理谁,就传入谁的实例进行代理。
两种代理模式
1.静态代理:
有程序员创建或工具类生成代理类的源码,再编译代理类。静态指在运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前已经确定。上面的例子就是一个静态代理。
静态代理类的优点:业务类只需要关注业务逻辑本身,保证了业务逻辑的重用性,这是代理类的共有优点。
缺点:
1.代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多则需要一一为每种方法进行代理,这可是个重复繁琐的大工程,如果被代理的类规模稍大时代理类就无法胜任了。
2.如果接口增加一个方法,实现类和代理类都需要实现此方法,增加了代码维护的复杂度。
2.动态代理
动态代理类的源码在程序运行期间由JVM根据反射等机制动态生成,不错在代理类的字节码文件。代理类和委托类的关系是在运行时确定的。
1.与动态代理关联紧密的API
(1).java.lang.reflect.Proxy:这是Java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法为一组接口动态地生成代理类及其对象:
方法一:获取指定代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy);
方法二:用于获取关联于指定类装载器和一组接口的动态代理类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces);
方法三:用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl);
方法四:用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h);
(2) java.lang.reflect.InvocationHandler:调用处理器接口,自定义一个invoke方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。核心方法InvocationHandler语法:
//第一个参数是代理类实例,第二个参数是调用的方法对象,第三个是调用参数
//调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
Object invoke(Object proxy, Method method, Object[] args);
(3) java.lang.ClassLoader:类装载器类,负责将类的字节码装载到java虚拟机中并为其定义类对象,然后该类才能被使用。和普通类的区别是字节码是由JVM在运行时动态生成的,而不是预存于一个class文件中。每次生成动态代理类对象时都需要指定一个类装载器对象。
反射实现动态代理:
public interface Moveable {
public void move();
}
public class Tank implements Moveable{
@Override
public void move() {
int a = 5;
int b = 6;
int c = 0;
int d = 0;
for (int i=0; i<1000; i++) {
d = i;
}
c = ((a+b)/2)*12;
System.out.println("Tank速度是:" + c);
}
}
public class Proxy {
public static Object newProxyIntenct(Class clazz, InvocationHandler h)
throws Exception {
String br ="\r\n";
StringBuffer methString = new StringBuffer();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
methString.append(" @Override" + br + "");
methString.append(" public void " + method.getName() + " (){" + br);
methString.append(" try{" + br);
methString.append(" Method md =" + clazz.getName() +
".class.getMethod(\"" + method.getName() + "\");" + br);
methString.append( "h.invoke(this,md);" + br);
methString.append(" ");
methString.append(" } catch (Exception e) {");
methString.append(" e.printStackTrace();" + br);
methString.append(" }" + br);
methString.append(" }");
}
StringBuffer src = new StringBuffer();
src.append("package com.designpattern.proxy_dynamic;" + br);
src.append("import java.lang.reflect.Method;" + br);
src.append("public class $Proxy implements " + clazz.getName() + "{" + br);
src.append(" private com.designpattern.proxy_dynamic.InvocationHandler h;");
//构造函数开始
src.append(" public $Proxy(InvocationHandler h) {");
src.append(" super();" + br);
src.append(" this.h = h;" + br);
src.append(" }" +br);
//构造函数结束
//追加代理方法,拦截被代理对象的方法调用
src.append(methString);
src.append("}");
MakFileUtil.createFile("E:/JavaSEWorkSpace/DesignPatternDemo/src/com/designpattern/proxy_dynamic");
String fileName = "E:\\JavaSEWorkSpace\\DesignPatternDemo\\src\\com\\designpattern\\proxy_dynamic\\$Proxy.java";
System.out.println(fileName);
File file = new File(fileName);
FileWriter fWriter = new FileWriter(file);
fWriter.write(src.toString());
fWriter.flush();
fWriter.close();
//生成class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable units = fileManager.getJavaFileObjects(fileName);
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units);
task.call();
fileManager.close();
//装载到内存,生成新对象
URL[] urls = new URL[]{new URL("file:/" + "E:\\JavaSEWorkSpace\\DesignPatternDemo\\src\\")};
URLClassLoader loader = new URLClassLoader(urls);
Class c = loader.loadClass("com.designpattern.proxy_dynamic.$Proxy");
//通过有参构造器反射生成代理类的实例
Constructor ctr = c.getConstructor(InvocationHandler.class);
Object obj = ctr.newInstance(h);
return obj;
}
}
public class MakFileUtil {
public static void createFile(String pathstr) {
File dirFile;
boolean bFile;
bFile = false;
dirFile = new File("E:\\JavaSEWorkSpace\\DesignPatternDemo");
bFile = dirFile.exists();
if ( bFile == true) {
System.out.println("The folder exists;");
} else {
System.out.println("The folder do not exist, try to create a one...");
bFile = dirFile.mkdir();
if ( bFile == true ) {
System.out.println("Create successfully!");
} else {
System.out.println("Disable to make the folder, please check the disk is full or not.");
System.exit(1);
}
//创建多级目录
String path = pathstr;
StringTokenizer st = new StringTokenizer(path, "/");
String path1 = st.nextToken() + "/";
String path2 = path1;
while(st.hasMoreTokens()){
path1 = st.nextToken() + "/";
path2 += path1;
File inbox = new File(path2);
if( !inbox.exists() ){
inbox.mkdir();
}
}
}
}
}
/**
* 计时
* @author 张家骏
*
*/
public class TimeInvocationHandler implements InvocationHandler{
private Object target;
public TimeInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
long time1 = System.currentTimeMillis();
System.out.println("开始时间:" + time1);
try {
m.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
long time2 = System.currentTimeMillis();
System.out.println("开始时间:" + time2);
System.out.println("启动时间:" + (time2 - time1));
}
}
public class TankTest {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Tank t = new Tank();
Moveable moveable = (Moveable) Proxy.newProxyIntenct(Moveable.class,
new LogInvocationHandler(t)
);
moveable.move();
}
}
代理模式的优点:
1.职责清晰:真实角色实现实际的业务逻辑,无需关心其它非本职责的职务,通过后期的代理完成一件事务,使编程更加简洁清晰。
2.高扩展性:具体主题角色随时可能发生变化,只要实现了接口,无论怎么变化都逃脱不了接口控制,代理类可以不做任何修改。