手写实现精简版的jdk动态代理
一、前言
- 接上篇jdk动态代理原理分析来手写一个精简版的jdk动态代理。
- 上篇实现jdk动态代理的小总结:
- 1、基本校验
- 2、生成代理类
- 3、加载到jvm
- 4、构造方法实例化代理对象
- jdk动态代理其实还有缓存之类的,其他功能也更高级,本篇文章只实现核心。
二、精简版动态代理代码实现
-
代码实现基本上依然是按照小总结来:
- 1、定义自己的
类加载器
、InvocationHandler
、Proxy
- 2、基本校验
- 3、生成代理类,编译成class文件
- 4、加载到jvm,构造方法实例化代理对象
- 1、定义自己的
-
1.1、定义类加载器
ClassLoaderTest.java
,为了把生成的代理类加载到jvm内存中
。
package com.zzq.test.proxy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
/**
* 自定义的类加载器
*/
public class ClassLoaderTest extends ClassLoader {
private File dir;
public ClassLoaderTest(String path) {
dir = new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if(dir != null){
File classFile = new File(dir,name+".class");
if(classFile.exists()){
try {
FileInputStream fis = new FileInputStream(classFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte [] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1 ) {
baos.write(buffer,0,len);
}
//把字节流的内容加载到内存
return defineClass("com.zzq.test.proxy." +name,baos.toByteArray()
,0,baos.size());
} catch (Exception e) {
e.printStackTrace();
}
}
}
return super.findClass(name);
}
}
- 1.2 定义自己的
InvocationHandler
MyInvocationHandler.java
package com.zzq.test.proxy;
import java.lang.reflect.Method;
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args);
}
- 1.3 定义自己的Proxy
MyProxy.java
,具体逻辑先不写,就先定义个newProxyInstance
方法完事儿
package com.zzq.test.proxy;
public class MyProxy {
/**
* 返回代理实例的方法
* **/
public static Object newProxyInstance(ClassLoader loader,Class<?> claz,MyInvocationHandler h){
return null;
}
}
2、基本校验
/***
* 必须的验证
* */
private static void requiredValidate(ClassLoader loader, Class<?> claz, MyInvocationHandler h) {
if (null == loader || null == claz || null == h) {
throw new RuntimeException("参数异常");
}
}
- 3.1、生成代理类,并输出文件,文件我是输出到当前代码目录的,生成的java文件也没有删除。
/**
* 字符串拼接代理类
*/
private static String getProxyTest(Class<?> claz) {
Method[] methods = claz.getMethods();
StringBuffer sb = new StringBuffer();
sb.append("package com.zzq.test.proxy;" + rt);
sb.append("import java.lang.reflect.Method; " + rt);
sb.append("public class ProxyTest implements " + claz.getName() + "{");
sb.append(rt + "MyInvocationHandler h;" + rt);
sb.append("public ProxyTest(MyInvocationHandler h){" + rt);
sb.append("this.h = h;" + rt);
sb.append("}" + rt);
sb.append(getMethodString(methods, claz));
sb.append(rt + "}");
return sb.toString();
}
private static void outFile(String proxyClssStr) {
File file = new File(path);
try {
FileWriter fw = new FileWriter(file);
fw.write(proxyClssStr);
fw.flush();
fw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- 3.2 编译class文件,最终执行还是需要class文件的
private static void complierJava() throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileManager> javaFileObjects = (Iterable<? extends JavaFileManager>) standardJavaFileManager.getJavaFileObjects(path);
CompilationTask task = compiler.getTask(null, standardJavaFileManager, null, null, null, (Iterable<? extends JavaFileObject>) javaFileObjects);
task.call();
try {
standardJavaFileManager.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- 4、 加载到jvm内存,这里要用到上面写的自定义的类加载器了
private static Object loaderClassToJvm(MyInvocationHandler h) {
ClassLoaderTest loaderTest = new ClassLoaderTest(path.replace("/ProxyTest.java", ""));
try {
Class<?> clz = loaderTest.findClass("ProxyTest");
Constructor<?> construct = clz.getConstructor(MyInvocationHandler.class);
//把h参数传入代理类的构造方法 返回代理类的实例
return construct.newInstance(h);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
- 完整的代码
MyProxy.java
package com.zzq.test.proxy;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
public class MyProxy {
private static final String path = MyProxy.class.getResource("/").getPath().substring(1).replace("/target/test-classes/","") +"/src/test/java/com/zzq/test/proxy/ProxyTest.java";
//private static final String path = "/Volumes/新加卷/workspace/git/CeShi2/src/test/java/com/zzq/test/proxy/ProxyTest.java";
private static final String rt = "\r\n";
/**
* 返回代理实例的方法
**/
public static Object newProxyInstance(ClassLoader loader, Class<?> claz, MyInvocationHandler h) {
requiredValidate(loader, claz, h);
//拼接代理类
String proxyClssStr = getProxyTest(claz);
//输出到文件
//G:/win_workspace/git/CeShi2/src/test/java/com/zzq/test/proxy
outFile(proxyClssStr);
//编译文件
try {
complierJava();
} catch (IOException e) {
e.printStackTrace();
}
//加載到jvm内存
return loaderClassToJvm(h);
}
/***
* 必须的验证
* */
private static void requiredValidate(ClassLoader loader, Class<?> claz, MyInvocationHandler h) {
if (null == loader || null == claz || null == h) {
throw new RuntimeException("参数异常");
}
}
private static Object loaderClassToJvm(MyInvocationHandler h) {
ClassLoaderTest loaderTest = new ClassLoaderTest(path.replace("/ProxyTest.java", ""));
try {
Class<?> clz = loaderTest.findClass("ProxyTest");
Constructor<?> construct = clz.getConstructor(MyInvocationHandler.class);
//把h参数传入代理类的构造方法 返回代理类的实例
return construct.newInstance(h);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
private static void complierJava() throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileManager> javaFileObjects = (Iterable<? extends JavaFileManager>) standardJavaFileManager.getJavaFileObjects(path);
CompilationTask task = compiler.getTask(null, standardJavaFileManager, null, null, null, (Iterable<? extends JavaFileObject>) javaFileObjects);
task.call();
try {
standardJavaFileManager.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void outFile(String proxyClssStr) {
File file = new File(path);
try {
FileWriter fw = new FileWriter(file);
fw.write(proxyClssStr);
fw.flush();
fw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 字符串拼接代理类
*/
private static String getProxyTest(Class<?> claz) {
Method[] methods = claz.getMethods();
StringBuffer sb = new StringBuffer();
sb.append("package com.zzq.test.proxy;" + rt);
sb.append("import java.lang.reflect.Method; " + rt);
sb.append("public class ProxyTest implements " + claz.getName() + "{");
sb.append(rt + "MyInvocationHandler h;" + rt);
sb.append("public ProxyTest(MyInvocationHandler h){" + rt);
sb.append("this.h = h;" + rt);
sb.append("}" + rt);
sb.append(getMethodString(methods, claz));
sb.append(rt + "}");
return sb.toString();
}
/**
* 循环拼接处接口的方法(这里是UserService接口)
*/
private static Object getMethodString(Method[] methods, Class<?> claz) {
String proxyMe = "";
for (Method method : methods) {
proxyMe += "public void " + method.getName();
proxyMe += "() throws Throwable {" + rt + " Method md =";
proxyMe += claz.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt;
proxyMe += " this.h.invoke(this,md,null);" + rt + "}" + rt;
}
return proxyMe;
}
}
- 到这里核心已经写完了,下面就是写代码去测试这个精简版的动态代理了。
三、编写测试代码
-
上面已经写完了精简版的动态代理,下面开始写测试代码。
-
依旧把步骤列出来:
- 1、写
service
和service实现类
- 2、写
MyInvocationHandler
的实现类 - 3、测试代码
调用精简版的动态代理
- 1、写
-
1、
UserService.java
和UserServiceImpl.java
package com.zzq.test.proxy;
public interface UserService {
public void test() throws Throwable ;
}
package com.zzq.test.proxy;
public class UserServiceImpl implements UserService{
@Override
public void test() throws Throwable {
System.out.println("进入test方法");
}
}
- 2、
MyCustomInvocationHandler.java
package com.zzq.test.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyCustomInvocationHandler implements MyInvocationHandler {
UserService userService;
public MyCustomInvocationHandler( UserService userService) {
this.userService = userService;
}
public Object invoke(Object proxy,Method method,Object []args){
try {
System.out.println("before");
method.invoke(userService, args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
- 3、测试代码
Test.java
package com.zzq.test.proxy;
public class Test {
public static void main(String[] args) {
UserService service = (UserService) MyProxy.newProxyInstance(Test.class.getClassLoader()
, UserService.class, new MyCustomInvocationHandler(new UserServiceImpl()));
try {
//service是一个生成的代理类
service.test();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 运行测试:
- 这里有一个
warning
,可以不管它。 - 再来看看生成的
ProxyTest.java
文件,我格式化一下。
package com.zzq.test.proxy;
import java.lang.reflect.Method;
public class ProxyTest implements com.zzq.test.proxy.UserService {
MyInvocationHandler h;
public ProxyTest(MyInvocationHandler h) {
this.h = h;
}
public void test() throws Throwable {
Method md = com.zzq.test.proxy.UserService.class.getMethod("test", new Class[]{});
this.h.invoke(this, md, null);
}
}
- 和jdk动态代理原理几乎一致,我得到的
service
对象就是这个ProxyTest
对象,调用service.test();
h
对象就是我们传进来的MyCustomInvocationHandler
对象,调用invoke
方法
最后method.invoke(userService, args);
method
看前面ProxyTest
的代码就可以知道是service实现类的test方法
,调用到UserServiceImpl#test
方法
四、小总结
-
整个核心就在于:
生成代理类
、编译class文件
、加载到jvm内存中
、实例化代理对象
。 -
本人水平有限,如果文章有误的地方,希望批评指正,感谢您的观看。