代理模式 : 给某一对象提供代理对象,并由代理对象控制具体对象的引用.
一、代理模式涉及的角色:
(1)抽象主题角色:声明了代理主题和真实主题的公共接口,使任何需要真实主题的地方都能用代理主题代替.
(2)真实代理对象:定义了代理角色所代表的具体对象.
(3)Proxy产生代理主题角色的类,根据不同的主题接口,动态产生加载相应的RealProxy并实现主题接口所定义的抽象方法
(4)InvocationHandler用户自定义需要为真实代理对象代理的处理逻辑
二、动态代理设计模式基本UML类图
接下来附上具体的实现代码附注解
抽象主题类,被代理的目标
package com.hust.edu.dynProxy;
/**
* 在静态代理中,要对多个类进行代理,就要创建多个代理类,并且随着类的增加,创建的代理类将更多,造成类的数目膨胀,逻辑重复,不维护
*
* 因此,利用Java反射原理,更加优雅的实现代理的一种模式,就是动态代理,让你可以在不修改源码的情况下,增强一些方法,实现代码扩展
*
* Spring框架就是利用这里原理实现AOP编程模式(有兴趣可以研究下)
*
*/
/**
* 抽象主题类,被代理的目标
*/
public interface ISubject {
public void excute();
}
具体主题对象
package com.hust.edu.dynProxy;
/**
* 具体主题对象
*/
public class RealSubject implements ISubject{
@Override
public void excute() {
System.out.println("do my work.......");
}
}
InvocationHandler 抽象接口
package com.hust.edu.dynProxy;
import java.lang.reflect.Method;
/**
* InvocationHandler 抽象接口
*/
public interface InvocationHandler {
public void invoke(Object o, Method m);
}
InvocationHandler 抽象基类
package com.hust.edu.dynProxy;
import java.lang.reflect.Method;
/**
* InvocationHandler 抽象基类,主要目标是实现多种业务的重叠代理
*
*/
public abstract class AbInvocationHandler implements InvocationHandler{
protected Object target;
public AbInvocationHandler(Object taObject){
this.target = taObject;
}
public void superInvoke(Object object, Method method) {
try {
/** 如果目标对象还是InvocationHandler 继续调用InvocationHandler的invoke **/
if (target instanceof InvocationHandler) {
((InvocationHandler) target).invoke(object, method);
} else {
method.invoke(target);
}
} catch (Exception e) {
}
}
}
代理实现处理 实现类InvocationHandler的子类
package com.hust.edu.dynProxy;
import java.lang.reflect.Method;
import java.util.Date;
/**
* 实现目标对象函数的处理时间代理逻辑
*/
public class TimeHandler extends AbInvocationHandler {
public TimeHandler(Object taObject) {
super(taObject);
}
public void invoke(Object object, Method method) {
long start = System.currentTimeMillis();
System.out.println("starttime : " + new Date(start));
superInvoke(object, method);
long end = System.currentTimeMillis();
/** 输出目标函数的运行时间 **/
System.out.println("endtime : " + new Date(end));
}
}
package com.hust.edu.dynProxy;
import java.lang.reflect.Method;
/**
* 实现事务处理日志代理逻辑
*/
public class TransactionHandler extends AbInvocationHandler {
public TransactionHandler(Object taObject) {
super(taObject);
}
public void invoke(Object object, Method method) {
System.out.println("Transaction Start");
superInvoke(object, method);
System.out.println("Transaction Commit");
}
}
动态产生代理类
package com.hust.edu.dynProxy;
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 {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
String methodStr = "";
String rt = "\r\n";
Method[] methods = infce.getMethods();
/**
* 这一大段拼字符串的逻辑主要目的是拼接处代理类的java源代码 ,其实这也是动态的核心思想,就是将原来手动创建代理类的过程,改成程序自动生成
* 步骤:
*
* 1. 拼接出实现抽象主题方法的函数体
*
* 2. 拼接处类的定义、实现的接口和构造函数
*
* 3. 将拼接的Java代码写入本地文件
**/
for (Method m : methods) {
methodStr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
" try {" + rt +
" Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
" h.invoke(this, md);" + rt +
" }catch(Exception e) {e.printStackTrace();}" + rt +
"}";
}
String src =
"package com.hust.edu.dynProxy;" + rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy1 implements " + infce.getName() + "{" + rt +
" public $Proxy1(InvocationHandler h) {" + rt +
" this.h = h;" + rt +
" }" + rt +
" com.hust.edu.dynProxy.InvocationHandler h;" + rt +
methodStr +
"}";
String fileName = "E:/Test/com/hust/edu/dynProxy/$Proxy1.java";
File f = new File(fileName);
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
/** 将字符串代码编译成.class字节码,保存在本地 **/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(fileName);
CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
/** 从本地.class文件中加载,产生代理对象 **/
URL[] urls = new URL[] { new URL("file:/" + "E:/Test/") };
@SuppressWarnings("resource")
URLClassLoader ul = new URLClassLoader(urls);
Class mc = ul.loadClass("com.hust.edu.dynProxy.$Proxy1");
/** 调用构造函数,创建代理类实例对象 **/
Constructor ctr = mc.getConstructor(InvocationHandler.class);
Object m = ctr.newInstance(h);
return m;
}
}
客户端:
package com.hust.edu.dynProxy;
public class Client {
public static void main(String args[]) throws Exception{
RealSubject real = new RealSubject();
InvocationHandler h0 = new TimeHandler(real);
//实现叠加的代理h0、分别前后被h1、h2 代理执行
InvocationHandler h1 = new TransactionHandler(h0);
ISubject u = (ISubject) Proxy.newProxyInstance(ISubject.class, h1);
u.excute();
}
}
由图可以看出对目标类实现了两次包裹的效果