最近在深入学习代理模式
尝试了一下JDK动态代理的简单源码分析,从源码进行分析感觉对动态代理的实现更加清晰,特此记录
先分析一下JDk代理的实现。
条件: 代理人,被代理人,代理做的事
流程:获取被代理人信息生成代理实例完成代理工作
JDk代理中,代理人通过实现InvocationHandler来完成代理
先看看InvocationHandler做了什么。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
通过源码我们不难看出,代理人通过实现invoke方法完成代理工作
因此,我们可以自定义一个InvocationHandler---->MyInvocationHandler
package com.Mrxuuu.st.proxy.custom.MyProxy;
import java.lang.reflect.Method;
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
上面说了,代理人要完成代理首先得获取被代理人信息生成代理人实例
实现过程如下
//代理对象先得获取被代理者的信息才能完成代理工作
private Person target;
//
public Object getInstance(Person target)throws Exception{
this.target=target;
//获取被代理对象类信息
Class<? extends Person> clazz = target.getClass();
//实例化代理对象
Object instance = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
return instance;
}
看看流程
Object instance = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
在获取代理信息后,通过Proxy.newProxyInstance(…)来获取实例
并且JDK代理生成了一个新的代理对象$Proxy0
我们通过字节码生成class文件 反编译看看$Proxy0都有些什么
import com.sun.proxy.*;
import java.lang.reflect.*;
public final class $proxy0 extends Proxy implements $Proxy0
{
private static Method m1;
private static Method m5;
private static Method m2;
private static Method m6;
private static Method m11;
private static Method m13;
private static Method m0;
private static Method m8;
private static Method m12;
private static Method m7;
private static Method m10;
private static Method m4;
private static Method m9;
private static Method m3;
public $proxy0(final InvocationHandler invocationHandler) {
super(invocationHandler);
}
public final boolean equals(final Object o) {
try {
return (boolean)super.h.invoke(this, $proxy0.m1, new Object[] { o });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final InvocationHandler getInvocationHandler(final Object o) throws IllegalArgumentException {
try {
return (InvocationHandler)super.h.invoke(this, $proxy0.m5, new Object[] { o });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, $proxy0.m2, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final Class getProxyClass(final ClassLoader classLoader, final Class[] array) throws IllegalArgumentException {
try {
return (Class)super.h.invoke(this, $proxy0.m6, new Object[] { classLoader, array });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final Class getClass() {
try {
return (Class)super.h.invoke(this, $proxy0.m11, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final void notifyAll() {
try {
super.h.invoke(this, $proxy0.m13, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final int hashCode() {
try {
return (int)super.h.invoke(this, $proxy0.m0, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final void wait() throws InterruptedException {
try {
super.h.invoke(this, $proxy0.m8, null);
}
catch (Error | RuntimeException | InterruptedException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final void notify() {
try {
super.h.invoke(this, $proxy0.m12, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final Object newProxyInstance(final ClassLoader classLoader, final Class[] array, final InvocationHandler invocationHandler) throws IllegalArgumentException {
try {
return super.h.invoke(this, $proxy0.m7, new Object[] { classLoader, array, invocationHandler });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final void wait(final long n) throws InterruptedException {
try {
super.h.invoke(this, $proxy0.m10, new Object[] { n });
}
catch (Error | RuntimeException | InterruptedException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final boolean isProxyClass(final Class clazz) {
try {
return (boolean)super.h.invoke(this, $proxy0.m4, new Object[] { clazz });
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final void wait(final long n, final int n2) throws InterruptedException {
try {
super.h.invoke(this, $proxy0.m9, new Object[] { n, n2 });
}
catch (Error | RuntimeException | InterruptedException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
public final void doSth() {
try {
super.h.invoke(this, $proxy0.m3, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
static {
try {
$proxy0.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
$proxy0.m5 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getInvocationHandler", Class.forName("java.lang.Object"));
$proxy0.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
$proxy0.m6 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getProxyClass", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"));
$proxy0.m11 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getClass", (Class<?>[])new Class[0]);
$proxy0.m13 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notifyAll", (Class<?>[])new Class[0]);
$proxy0.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
$proxy0.m8 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", (Class<?>[])new Class[0]);
$proxy0.m12 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notify", (Class<?>[])new Class[0]);
$proxy0.m7 = Class.forName("com.sun.proxy.$Proxy0").getMethod("newProxyInstance", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler"));
$proxy0.m10 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE);
$proxy0.m4 = Class.forName("com.sun.proxy.$Proxy0").getMethod("isProxyClass", Class.forName("java.lang.Class"));
$proxy0.m9 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE, Integer.TYPE);
$proxy0.m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("doSth", (Class<?>[])new Class[0]);
}
catch (NoSuchMethodException ex) {
throw new NoSuchMethodError(ex.getMessage());
}
catch (ClassNotFoundException ex2) {
throw new NoClassDefFoundError(ex2.getMessage());
}
}
}
内容很简单,都是一些类里面的方法。也就是说,代理对象,读取了被代理类的所有信息,然后又新生成了一个类。 JDK代理也就是生成一个新的对象去帮助我原来的对象做事。
那么思路来了。 我们可以自己在读取被代理类的字节码后,自己生成一个新的代理类。然后自己编译,加载到JVM.这样的话,是不是自己就完成了一个JDK代理。
思路有了就好办了。
那么我们就可以开始自定义Proxy------->MyProxy
Proxy主要实现了代理实例的生成。
newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandler h)
从以上方法我们可以看出来。要实现新实例,我们还得有自己的ClassLoader
这里我们通过findClass来加载我们要生成的class.
到这里,分析步骤齐活。
说干就干。
首先定义自己的ClassLoader
//代码生成
public class MyClassLoader extends ClassLoader{
private File baseDir;
public MyClassLoader() {
String basePath=MyClassLoader.class.getResource("").getPath();
this.baseDir=new File(basePath);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
System.out.println(name);
String className=MyClassLoader.class.getPackage().getName()+"."+name;
System.out.println(className);
if(baseDir!=null) {
File classFile=new File(baseDir,name.replaceAll("\\.", "/")+".class");
System.out.println(classFile.getPath());
if(classFile.exists()) {
FileInputStream in=null;
try {
in=new FileInputStream(classFile);
ByteArrayOutputStream out=new ByteArrayOutputStream();
byte[] buff=new byte[1024];
int len;
while((len=in.read(buff))!=-1)
{
out.write(buff,0,len);
}
return defineClass(className,out.toByteArray(),0,out.size());
} catch (Exception e) {
// TODO: handle exception
}finally {
if(null!=in)
{
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
return null;
}
}
目的是将我们生产的代理对象加载到JVM(这里使用findClass 而不是loadClass,大家可以去查查二者的区别)
我们再来看看自定义生产示例的方法
newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandler h)
现在该定义MyInvocationHandler
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
好像我们需要定义的都已经齐全了。
再捋一捋我们说过的代理的大致流程
获取被代理类信息,然后生产代理对象,然后完成代理工作
既然定义完了,现在我们来看看怎么怎么生产示例对象
代码如下:
//生成代理对象代码
public class MyProxy{
private static String rn="\r\n";
public static Object newProxyInstance(MyClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandler h) throws Exception {
//1.生成源代码
String proxySrx=generatorSrc(interfaces[0]);
//2.将代码输出到磁盘,作为Java文件
String filePath= MyProxy.class.getResource("").getPath();
File file=new File(filePath+"$Proxy0.java");
FileWriter fw=new FileWriter(file);
fw.write(proxySrx);
fw.close();
//3.编译源代码,生成class
//获取编译器
JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager=compiler.getStandardFileManager(null, null, null);
Iterable iterable=manager.getJavaFileObjects(file);
CompilationTask task=compiler.getTask(null, manager, null, null, null, iterable);
task.call();
manager.close();
//4. 动态加载到jvm 返回代理对象
Class proxyClass=loader.findClass("$Proxy0");
Constructor c=proxyClass.getConstructor(MyInvocationHandler.class);
return c.newInstance(h);
}
//生成代理类
public static String generatorSrc(Class<?> interfaces) {
StringBuffer src=new StringBuffer();
src.append("package com.Mrxuuu.st.proxy.custom.MyProxy;"+rn);
src.append("import java.lang.reflect.*;"+rn);
src.append("public final class $Proxy0 implements " +interfaces.getName()+"{"+rn);
src.append("MyInvocationHandler h;"+rn);
src.append("public $Proxy0(MyInvocationHandler h){"+rn);
src.append("this.h=h;"+rn);
src.append("}"+rn);
for(Method m:interfaces.getMethods())
{
src.append("public"+" "+m.getReturnType().getName()+" "+m.getName()+"(){"+rn);
src.append("try {"+rn);
src.append("Method m="+interfaces.getName()+".class.getMethod(\""+m.getName()+"\",new Class[]{});"+rn);
src.append("this.h.invoke(this,m,null);"+rn);
// if(!m.getReturnType().equals("void")) {
// src.append("return ''");
// }
src.append("} catch (Throwable e) {}"+rn);
src.append("}"+rn);
}
src.append("}");
return src.toString();
}
}
代码一长串,我们先来看看我的注释:
1.生成代理类的源代码
我们之前已经看过JDK代理过程中生成的代理类$proxy0
我们可以自定义 generatorSrc(Class<?> interfaces) 生成一个同样的代理类 $Proxy0
并保存为自定义的java文件。
2.编译源代码,生成class 动态加载到jvm
3.返回一个我们需要的代理对象
4.通过代理对象去完成代理工作。
em…代码不简单,但是思路很简单,关于JDK动态代理的简单源码分析与重写就到这