为了便于理解jdk动态代理,模仿jdk动态代理的思路,模拟写一个动态代理demo,V1思路如下:
- 拼接代理对象源码,使用文件流写出$Proxy.java文件
- 将$Proxy.java动态编译成.class文件
- $Proxy.class文件动态加载到JVM内存中,产生Class对象
- 使用反射构造出代理对象
- 用户调用代理对象实现方法代理
源码如下:
定义接口
package com.ant.myJdkProxy;
/**
* 接口,模拟有无返回值、有无参数多种情况方法
*/
public interface IndexDao {
void test();
void test(String s);
String testReturn(String s,Integer i);
}
定义实现类,即被代理对象:
package com.ant.myJdkProxy;
/**
* 接口实现类
*/
public class IndexDaoImpl implements IndexDao{
@Override
public void test() {
System.out.println("test()");
}
@Override
public void test(String s) {
System.out.println("Test("+s+")");
}
@Override
public String testReturn(String s, Integer i) {
System.out.println("testReturn("+s+","+i);
return "hshsh";
}
}
定义产生代理的工具类:
package com.ant.myJdkProxy;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* 代理工具类
* 手动模拟jdk动态代理
* 步骤:根据传进来的目标代理对象,动态实现代理逻辑的代理java对象文件
* .java文件-->编译成class文件-->加载至jvm虚拟机内存,称为class对象-->反射创建代理对象-->调用代理方法实现代理
*
*/
public class ProxyUtil {
/**
* 生成java文件,假设生成在D盘下,包名是com.ant
* @param target 需要被代理的目标对象
* @param interfc 目标对象实现的接口
* @return
*/
public static Object getProxy(Object target,Class interfc) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if(null == target || null == interfc){
throw new IllegalArgumentException("参数不允许为空");
}
if(!interfc.isInterface()){
throw new IllegalArgumentException("必须是接口");
}
//1.生成java源代码字符串
StringBuilder sb = genJavaStr(target,interfc);
//2.将源代码写成$Proxy.java至D:/com/ant下
File dir = new File("D:\\com\\ant");
if(!dir.exists()){
dir.mkdirs();
}
File file = new File("D:\\com\\ant\\$Proxy.java");
if(!file.exists()){
file.createNewFile();
}
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(sb.toString());
fileWriter.flush();
fileWriter.close();
//3.将$Proxy.java动态编译成字节码
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
//4.将class文件动态加载到jvm虚拟机内存中
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class<?> clazz = urlClassLoader.loadClass("com.ant.$Proxy");
//5.反射创建代理对象
Constructor<?> constructor = clazz.getConstructor(target.getClass());
return constructor.newInstance(target);
}
/**
* 生成源代码字符串
* package com.ant
* public class $Proxy implement interface{
* private Object target;
*
* public $Proxy (Object target){
* this.target = target;
* }
*
* //重写各个interface中的方法,并且在前后加上逻辑,执行目标对象的方法
* @Override
* public returnType methosName(args){
* System.out.println("before")
* returntype result = target.methosName(args);
System.out.println("after");
* return result;
* }
* }
*/
private static StringBuilder genJavaStr(Object target,Class interfc) {
String line = "\r\n";
String tab = "\t";
StringBuilder sb = new StringBuilder();
sb
.append("package com.ant;")
.append(line)
.append("public class $Proxy implements ").append(interfc.getName()).append(" {")
.append(line)
.append(tab)
.append("private ").append(target.getClass().getName()).append(" target;").append(line)
.append(tab).append("public $Proxy(").append(target.getClass().getName()).append(" target").append("){").append(line)
.append(tab).append(tab).append("this.target = target;").append(line)
.append(tab).append("}")
.append(line);
//方法
Method[] methods = interfc.getMethods();
if(null!=methods && methods.length>0){
for(Method m:methods){
sb.append(line).append(tab)
.append("@Override").append(line)
.append(tab)
//方法名称
.append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("(");
Class<?>[] parameterTypes = m.getParameterTypes();
//增加方法参数
String modalityParam = "";
if(null!=parameterTypes && parameterTypes.length>0){
StringBuilder sbu = new StringBuilder();
//var1,var2,var3 目标对象方法调用时使用
StringBuilder modalitySbu = new StringBuilder();
for(int i=0;i<parameterTypes.length;i++){
sbu.append(" ").append(parameterTypes[i].getName()).append(" var"+i).append(",");
modalitySbu.append("var"+i).append(",");
}
String temp = sbu.toString();
sb.append(temp.substring(0,sbu.lastIndexOf(",")));
String tempPa = modalitySbu.toString();
modalityParam = tempPa.substring(0,tempPa.lastIndexOf(","));
}
sb.append("){").append(line)
.append(tab).append(tab)
.append("System.out.println(\"方法逻辑处理之前\")").append(";").append(line)
.append(tab).append(tab);
if(void.class!=m.getReturnType()){
sb.append("return ");
}
sb.append("target.").append(m.getName()).append("(").append(modalityParam).append(");").append(line)
.append(tab).append("}");
}
}
sb.append(line).append("}");
return sb;
}
}
测试主函数:
package com.ant.myJdkProxy;
/**
* 测试主函数
*/
public class Main {
public static void main(String[] args){
IndexDao indexDao = new IndexDaoImpl();
try {
IndexDao proxy = (IndexDao)ProxyUtil.getProxy(indexDao, IndexDao.class);
proxy.test();
proxy.test("success");
proxy.testReturn("success",2333);
} catch (Exception e) {
e.printStackTrace();
}
}
}
到目前为止我们简单的动态代理已经写完,但是V1版本存在的问题:代理逻辑是我们写死的,无法达到用户自行控制代理逻辑,即用户无法通过先执行类似于加锁,执行被代理逻辑,再解锁这种方式控制,总得来说代理逻辑比较死,因此我们继续向下升级我们的动态代理逻辑,思路如下:我们通过定义类似于jdk的InvocationHandler方式定义一个我们自己的接口MyInvocationHandler,当用户执行代理对象的方法时,实际上是执行MyInvocationHandler.invoke方法,在invoke方法,我们将目标对象,目标对象执行的Method对象以及方法参数传给用户,由用户自行调用Method.invoke()方法,同时用户可以在调用invoke方法之前和之后增加自己的自定义逻辑,来实现完全与jdk动态代理相同的灵活性:
目标接口:
package com.ant.myJdkProxy.v2;
/**
* 接口,模拟有无返回值、有无参数多种情况方法
*/
public interface IndexDao {
void test() throws Throwable;
void test(String s);
String testReturn(String s, Integer i);
}
接口实现类:
package com.ant.myJdkProxy.v2;
/**
* 接口实现类
*/
public class IndexDaoImpl implements IndexDao {
@Override
public void test() {
System.out.println("test()");
}
@Override
public void test(String s) {
System.out.println("Test("+s+")");
}
@Override
public String testReturn(String s, Integer i) {
System.out.println("testReturn("+s+","+i);
return "hshsh";
}
}
自定义的InvocationHandler接口:
package com.ant.myJdkProxy.v2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public interface MyInvocationHandler {
/**
* 模拟jdk InvocationHandler,实际上为了能够让用户自定义代理逻辑,提供给用户的钩子函数,由用户定义代理逻辑与真实方法调用的顺序
* 当用户获取到代理对象调用方法时,代理对象方法中实际上是调用本方法
* @param method
* @param args
* @return
*/
Object invoke(Object target,Method method,Object...args) throws InvocationTargetException, IllegalAccessException;
}
用户使用代理时,自行创建的InvocationHandler实现,用于实现代理逻辑:
package com.ant.myJdkProxy.v2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IndexInvocationHandler implements MyInvocationHandler {
@Override
public Object invoke(Object target, Method method, Object... args) throws InvocationTargetException, IllegalAccessException {
System.out.println("方法执行开始前");
Object o = method.invoke(target, args);
System.out.println("方法执行开始后");
return o;
}
}
代理工具类:
package com.ant.myJdkProxy.v2;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
/**
* 代理工具类
* 手动模拟jdk动态代理
* 步骤:根据传进来的目标代理对象,动态实现代理逻辑的代理java对象文件
* .java文件-->编译成class文件-->加载至jvm虚拟机内存,称为class对象-->反射创建代理对象-->调用代理方法实现代理
*/
public class ProxyUtil {
/**
* 生成java文件,假设生成在D盘下,包名是com.ant
*
* @param target 需要被代理的目标对象
* @param interfc 目标对象实现的接口
* @return
*/
public static Object getProxy(Object target, Class interfc, MyInvocationHandler invocationHandler) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (null == target || null == interfc) {
throw new IllegalArgumentException("参数不允许为空");
}
if (!interfc.isInterface()) {
throw new IllegalArgumentException("必须是接口");
}
//1.生成java源代码字符串
StringBuilder sb = genJavaStr(target, interfc, invocationHandler);
//2.将源代码写成$Proxy.java至D:/com/ant下
File file = createJavaFile(sb.toString());
//3.将$Proxy.java动态编译成字节码
compile(file);
//4.将class文件动态加载到jvm虚拟机内存中
Class clazz = loadClass();
//5.反射创建代理对象
Constructor<?> constructor = clazz.getConstructor(target.getClass(), MyInvocationHandler.class);
return constructor.newInstance(target,invocationHandler);
}
/**
* 生成源代码字符串
* package com.ant
* public class $Proxy implement interface{
* private Object target;
* <p>
* public $Proxy (Object target){
* this.target = target;
* }
* <p>
* //重写各个interface中的方法,并且在前后加上逻辑,执行目标对象的方法
*
* @Override public returnType methosName(args){
* System.out.println("before")
* returntype result = target.methosName(args);
* System.out.println("after");
* return result;
* }
* }
*/
private static StringBuilder genJavaStr(Object target, Class interfc, MyInvocationHandler invocationHandler) {
String line = "\r\n";
String tab = "\t";
StringBuilder sb = new StringBuilder();
sb
.append("package com.ant;")
.append(line)
.append("public class $Proxy implements ").append(interfc.getName()).append(" {")
.append(line)
.append(tab)
.append("private ").append(target.getClass().getName()).append(" target;").append(line)
.append("private ").append("com.ant.myJdkProxy.v2.MyInvocationHandler invocationHandler;").append(line)
.append(tab).append("public $Proxy(").append(target.getClass().getName()).append(" target").append(",").append("com.ant.myJdkProxy.v2.MyInvocationHandler invocationHandler").append("){").append(line)
.append(tab).append(tab).append("this.target = target;").append(line)
.append(tab).append(tab).append("this.invocationHandler = invocationHandler;").append(line)
.append(tab).append("}")
.append(line);
//方法
Method[] methods = interfc.getMethods();
if (null != methods && methods.length > 0) {
for (Method m : methods) {
sb.append(line).append(tab)
.append("@Override").append(line)
.append(tab)
//方法名称
.append("public ").append(m.getReturnType().getName()).append(" ").append(m.getName()).append("(");
Class<?>[] parameterTypes = m.getParameterTypes();
//增加方法参数
String modalityParam = "";
String reflectParam = "";
if (null != parameterTypes && parameterTypes.length > 0) {
StringBuilder sbu = new StringBuilder();
//var1,var2,var3 目标对象方法调用时使用
StringBuilder modalitySbu = new StringBuilder();
for (int i = 0; i < parameterTypes.length; i++) {
sbu.append(" ").append(parameterTypes[i].getName()).append(" var" + i).append(",");
modalitySbu.append("var" + i).append(",");
reflectParam = reflectParam + parameterTypes[i].getName() + ".class";
reflectParam += ",";
}
String temp = sbu.toString();
sb.append(temp.substring(0, sbu.lastIndexOf(",")));
String tempPa = modalitySbu.toString();
modalityParam = tempPa.substring(0, tempPa.lastIndexOf(","));
reflectParam = reflectParam.substring(0, reflectParam.lastIndexOf(","));
}
sb.append("){").append(line)
.append(tab).append(tab);
//方法体
/*
try{
return (java.lang.String)invocationHandler.invoke(target,target.getDeclaredMethod("test",java.lang.String.class,java.lang.Integer.class),var0,var1,var2);
}cache(Throwable t){
t.printStackTrace();
}
return null;
*/
sb.append("try {").append(line).append(tab).append(tab).append(tab);
if (void.class != m.getReturnType()) {
sb.append("return ").append("(").append(m.getReturnType().getName()).append(")");
}
sb.append("invocationHandler.invoke(").append("target,")
.append("target.getClass().getDeclaredMethod(").append("\"").append(m.getName()).append("\"");
if (null != reflectParam && reflectParam != "") {
sb.append(",").append(reflectParam);
}
sb.append(")");
if (m.getParameterTypes() != null && m.getParameterTypes().length > 0) {
sb.append(",").append(modalityParam);
}
sb.append(");");
sb.append(line).append(tab).append("}catch(Throwable t){").append(line)
.append(tab).append(tab).append(tab).append("t.printStackTrace();").append(line)
.append(tab).append(tab).append("}").append(line);
if(void.class != m.getReturnType()){
sb.append(tab).append(tab).append("return null;");
}
sb.append(line).append(tab).append("}");
}
}
sb.append(line).append("}");
return sb;
}
/**
* 根据源代码,创建java文件
*
* @param sourceCode
* @return
* @throws IOException
*/
private static File createJavaFile(String sourceCode) throws IOException {
File dir = new File("D:\\com\\ant");
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File("D:\\com\\ant\\$Proxy.java");
if (!file.exists()) {
file.createNewFile();
}
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(sourceCode);
fileWriter.flush();
fileWriter.close();
return file;
}
/**
* 将源代码编译成字节码文件
*
* @param file
* @throws IOException
*/
private static void compile(File file) throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
}
/**
* 动态加载class文件到jvm内存中
*
* @return
*/
private static Class loadClass() throws MalformedURLException, ClassNotFoundException {
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class<?> clazz = urlClassLoader.loadClass("com.ant.$Proxy");
return clazz;
}
}
测试类:
package com.ant.myJdkProxy.v2;
/**
* 测试主函数
*/
public class Main {
public static void main(String[] args){
IndexDao indexDao = new IndexDaoImpl();
try {
IndexDao proxy = (IndexDao) ProxyUtil.getProxy(indexDao, IndexDao.class, new IndexInvocationHandler() );
proxy.test();
proxy.test("2333");
proxy.testReturn("牛逼",2333);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
我们自己实现的jdk动态代理只是demo级别的,有很多内容都没有考虑到,其次在性能上与远程的jdk动态代理完全无法比,因为我们实现的动态代理涉及到多次IO操作,而jdk动态代理直接产生字节码数组(byte[]),然后直接native方法产生Class对象,通过Class对象反射产生代理对象,速度是我们demo的97倍,测试代码如下:
package com.ant.myJdkProxy.jdk;
import com.ant.myJdkProxy.v2.IndexDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JdkInvocationHandler implements InvocationHandler {
private IndexDao indexDao;
public JdkInvocationHandler(IndexDao indexDao){
this.indexDao = indexDao;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk before");
Object result = method.invoke(indexDao,args);
System.out.println("jdk after");
return result;
}
}
package com.ant.myJdkProxy.v2;
import com.ant.myJdkProxy.jdk.JdkInvocationHandler;
import java.lang.reflect.Proxy;
/**
* 测试主函数
* 经过测试我们自己写的动态代理执行时间是jdk动态代理的97倍
*/
public class Main {
public static void main(String[] args){
IndexDao indexDao = new IndexDaoImpl();
try {
//1973982603
long start1 = System.nanoTime();
IndexDao proxy = (IndexDao) ProxyUtil.getProxy(indexDao, IndexDao.class, new IndexInvocationHandler() );
proxy.test();
proxy.test("2333");
proxy.testReturn("牛逼",2333);
System.out.println(System.nanoTime()-start1);
//20197073
long start2 = System.nanoTime();
IndexDao indexDao1 = (IndexDao) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{IndexDao.class}, new JdkInvocationHandler(new IndexDaoImpl()));
indexDao1.test();
indexDao1.test("2333");
indexDao1.testReturn("牛逼",2333);
System.out.println(System.nanoTime()-start2);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}