用法
java动态代理机制可以实现使用一个抽象的中介类对任意类的任意方法进行进行代理,大致原理是运行时生成一个代理类,代理类再调用委托对象。
创建委托对象的接口
1 2 3 4
| public interface Sell { void sell(); void add(); }
|
创建接口的实现
1 2 3 4 5 6 7 8 9 10 11
| public class SellImpl implements Sell { @Override public void sell() { System.out.println("In sell method."); }
@Override public void add() { System.out.println("In add method."); } }
|
实现中介类
中介类实现java.lang.reflect.InvocationHandler接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class MyInvocationHandler implements InvocationHandler { private Object object;
public DynamicProxy(Object object){ this.object=object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { System.out.println("Before"); Object result = method.invoke(object, args); System.out.println("After"); return result; } }
|
用户调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Main { public static void main(String[] args) { MyInvocationHandler inter = new MyInvocationHandler(new SellImpl()); System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter));
sell.sell(); sell.add(); } }
|
原理分析
- 动态生成代理类,并且动态编译,再通过反射创建对象并加载到内存中:
graph LR
proxy[Proxy.newProxyInstance] -- JavaPoet --> java[C$Proxy0.java]
java -- Compile --> clazz[C$Proxy0.class]
clazz -- reflect --> sell[C$Proxy0]
- 在调用时,C$Proxy0代理InvocationHandler,InvocationHander代理subject。
生成代理类源码
newProxyInstance就是将中介类生成代理类源代码的方法,生成的代理类如下,可以看到其包含一个InvocationHander类,实现subject的接口,使用反射调用InvocationHander:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.lang.Override; import java.lang.reflect.Method;
class Proxy0 implements Sell { private InvocationHandler handler;
public Proxy0(InvocationHandler handler) { this.handler=handler; }
@Override public void add() { try { Method method = MyImpl.Sell.class.getMethod("add"); this.handler.invoke(this, method, null); } catch(Exception e) { e.printStackTrace(); } }
@Override public void sell() { try { Method method = MyImpl.Sell.class.getMethod("sell"); this.handler.invoke(this, method, null); } catch(Exception e) { e.printStackTrace(); } } }
|
可以使用JavaPoet实现,代码来自10分钟看懂动态代理设计模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec;
import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import javax.lang.model.element.Modifier; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader;
public class Proxy {
public static Object newProxyInstance(Class subject,InvocationHandler handler) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder("Proxy0") .addSuperinterface(subject);
FieldSpec fieldSpec = FieldSpec.builder(InvocationHandler.class, "handler", Modifier.PRIVATE).build(); typeSpecBuilder.addField(fieldSpec);
MethodSpec constructorMethodSpec = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(InvocationHandler.class, "handler") .addStatement("this.handler=handler") .build(); typeSpecBuilder.addMethod(constructorMethodSpec);
Method[] methods = subject.getDeclaredMethods(); for (Method method : methods) { MethodSpec methodSpec = MethodSpec.methodBuilder(method.getName()) .addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class) .returns(method.getReturnType()) .addCode("try {\n") .addStatement("\t$T method = " + subject.getName() + ".class.getMethod(\"" + method.getName() + "\")", Method.class) // 为了简单起见,这里参数直接写死为空 .addStatement("\tthis.handler.invoke(this, method, null)") .addCode("} catch(Exception e) {\n") .addCode("\te.printStackTrace();\n") .addCode("}\n") .build(); typeSpecBuilder.addMethod(methodSpec); }
JavaFile javaFile = JavaFile.builder("MyImpl", typeSpecBuilder.build()).build(); String srcPath="./MyProxy"; javaFile.writeTo(new File(srcPath));
return obj; } }
|
编译Proxy0的源代码
1 2 3 4 5 6 7 8 9 10
| public class JavaCompiler { public static void compile(File javaFile) throws IOException { javax.tools.JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null); Iterable iterable = fileManager.getJavaFileObjects(javaFile); javax.tools.JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, null, null, null, iterable); task.call(); fileManager.close(); } }
|
在Proxy.newProxyInstance()中调用:
1
| JavaCompiler.compile(new File(srcPath+"/MyImpl/Proxy0.java"));
|
加载进内存并创建对象
使用URLClassLoader加载.class文件
1 2 3 4 5 6 7
| File filpath=new File(""); URL[] urls = new URL[] {new URL("file:"+filpath.getAbsoluteFile()+"\\MyProxy\\")}; URLClassLoader classLoader = new URLClassLoader(urls); Class clazz = classLoader.loadClass("MyImpl.Proxy0"); Constructor constructor = clazz.getConstructor(InvocationHandler.class); constructor.setAccessible(true); Object obj = constructor.newInstance(handler);
|
使用
可以像系统内置的那样设置动态代理
1
| Sell sell = (Sell)Proxy.newProxyInstance(Sell.class, new MyInvocationHandler(new SellImpl()));
|
参考文献