首先说明我的个人观点:动态代理是对装饰者模式的升级
1、我们先来写一个装饰者模式的简单demo
一个接口
package decorateTest;
public interface Animal {
void sleep();
void eat();
}
一个被装饰类
package decorateTest;
public class Cat implements Animal {
@Override
public void sleep() {
System.out.println("sleep.............");
}
@Override
public void eat() {
System.out.println("eat..........");
}
}
一个装饰者
package decorateTest;
public class CatDecorator implements Animal {
private Animal origin;
public CatDecorator() {
super();
}
public CatDecorator(Animal inter) {
this.origin = inter;
}
@Override
public void sleep() {
System.out.println("kunle..................");
origin.sleep();
}
@Override
public void eat() {
origin.eat();
}
}
装饰者的代码逻辑比较相似,可以作如下优化
package invocateTest;
import java.lang.reflect.Method;
public class CatDecorator implements Animal {
private Animal origin;
public CatDecorator() {
super();
}
public CatDecorator(Animal inter) {
this.origin = inter;
}
@Override
public void sleep() {
invoke("sleep");
}
@Override
public void eat() {
invoke("eat");
}
private void invoke(String methodName) {
try {
Method method = origin.getClass().getMethod(methodName, null);
if (methodName.equals("sleep")) {
System.out.println("kunle...................");
}
method.invoke(origin, null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2、思考:如果我对方法怎样加强不确定,甚至加强哪个方法也不确定,该如何做?
解决办法:将invoke方法抽取出来以便需要时装配一个invoke方法
package invocateHandlerTest;
public class CatDecorator implements Animal {
private InvocationHandler handler;
public CatDecorator() {
super();
}
public CatDecorator(InvocationHandler handler) {
this.handler = handler;
}
@Override
public void sleep() {
handler.invoke("sleep");
}
@Override
public void eat() {
handler.invoke("eat");
}
}
package invocateHandlerTest;
import java.lang.reflect.Method;
public class InvocationHandler {
private Animal animal;
public InvocationHandler() {
super();
}
public InvocationHandler(Animal animal) {
this.animal = animal;
}
public void invoke(String methodName) {
try {
Method method = animal.getClass().getMethod(methodName, null);
if (methodName.equals("sleep")) {
System.out.println("kunle...................");
}
method.invoke(animal, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、思考:如果我并不知道要被加强的是哪个类,有什么接口,怎么办呢?
解决办法:在被装饰者和接口未知的情况下,设计一个工具类,根据传入的被装饰者和接口生成一个装饰类
package proxyTest;
import java.io.File;
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.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import org.apache.commons.io.FileUtils;
public class Proxy {
@SuppressWarnings("unchecked")
public static Object newInstance(Class interface1, InvocationHandler handler) {
Object newInstance = null;
String classSimpleName = interface1.getSimpleName() + "Proxy";
String className = "proxyTest." + classSimpleName;
// 拼装包名
String classString = "package proxyTest;";
// 拼装开头
classString += "public class " + classSimpleName + " implements " + interface1.getName()
+ "{private InvocationHandler handler;public " + classSimpleName + "(){super();}"//
+ "public " + classSimpleName + "(InvocationHandler handler) {this.handler = handler;}";
// 拼装方法
Method[] methods = interface1.getMethods();
for (Method method : methods) {
String methodname = method.getName();
String returnname = method.getReturnType().getName();
String modifierName = "public";
int modifiers = method.getModifiers();
if (modifiers == 1212) {// 如果 修饰符等于public就拼装public,修饰符是与一些常量数字一一对应的,记不住就不写了
modifierName = "private";
}
String methodBody = "{handler.invoke(\"" + method.getName() + "\");}";
classString = classString + modifierName + " " + returnname + " " + methodname + "()" + methodBody;
}
// 结束
classString += "}";
System.out.println(classString);
// 用拼接出来的装饰类的字符串生成一个class文件
Class clazz = generateClass(classString, classSimpleName, className);
try {
// 调用有参构造生成一个装饰者的实例
Constructor constructor = clazz.getConstructor(InvocationHandler.class);
constructor.setAccessible(true);
newInstance = constructor.newInstance(handler);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return newInstance;(这里就可以视为获得了动态代理的对象了)
}
/*
根据class文件的字符串内容写出一个java文件,再调用编译器将这个文件编译,并返回class对象,jdk的动态代理使用的是一个native方法
**/
private static Class generateClass(String classString, String classSimpleName, String className) {
String fileName = "H:\\Java\\javaee\\ProxyTest\\src\\proxyTest\\" + classSimpleName + ".java";
try {// 写出一个java文件
File file = new File(fileName);
FileUtils.writeByteArrayToFile(file, classString.getBytes());
// 编译这个java文件
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = jc.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(fileName);
CompilationTask cTask = jc.getTask(null, fileManager, null, null, null, fileObjects);
cTask.call();
fileManager.close();
// 使用URLClassLoader加载class到内存
String currentDir = System.getProperty("user.dir");
URL[] urls = new URL[] { new URL("file:/" + currentDir + "/src/") };
URLClassLoader cLoader = new URLClassLoader(urls);
Class<?> c = cLoader.loadClass(className);
cLoader.close();
return c;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
测试类
package proxyTest;
import org.junit.Test;
public class TestProxy {
@Test
public void test1() {
Animal animal = new Cat();
InvocationHandler handler = new InvocationHandler(animal);
Object newInstance = Proxy.newInstance(Animal.class, handler);
Animal animalProxy = (Animal) newInstance;
animalProxy.sleep();
}
}
4、最后还可以对handler做一个接口的抽取,就和常用的动态代理差不多了。
以上内容是我个人理解,欢迎讨论,如有雷同,一定是英雄所见略同(手动滑稽)