JDK动态代理,以及自己实现源码级动态代理粗例
-
1、关于动态代理
-
JDK动态代理: 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface),一个则是 Proxy(Class)类,这个类和接口是实现我们动态代理所必须用到的。主要用到了InvocationHandler类的Object invoke(Object proxy, Method method, Object[] args) throws Throwable方法,参数说明:
- proxy:指代我们所代理的那个真实对象,既动态生成的类的实例;
- method:指代的是主题接口的某个方法的Method对象;
- args:指代的是调用真实对象某个方法时接受的参数。
两种代理模式的区别:
- (1) JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
- (2) CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)。
两种代理模式的速度比较:
- (1)使用CGLIB实现动态代理,CGLIB底层采用ASM字节码生成框架,使用字节码技术生成动态代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLIB原理是动态生成被代理类的子类。
- (2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右
-
动态代理类:以示区别,我们将动态生成的类叫做动态代理类(注意代理类与动态代理类的区别:代理类用于获取目标类对象和增强代码插入,动态代理类是动态生成的类用于实例化真正执行增强代码的对象)
-
-
2、使用JDK类库实现动态代理
-
1)目标接口
public interface UsbDirve { /*U盘定价卖*/ int Sell(int count); }
-
2)目标实现类
public class UsbFactory implements UsbDirve{ @Override public int Sell(int count) { return 0; } }
-
3)代理类:实现InvocationHandler 接口的类
//代理类 public class UsbSellProxy implements InvocationHandler { //被代理对象引用 private Object obj = null; public UsbSellProxy(Object obj){ this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; //调用目标对象方法 Object data = method.invoke(obj, args); if(data != null) { int price = (int)data; price = price + 20; result = price; System.out.println("u盘多卖20块 :" + result); }else { return null; } return result; } }
-
4)测试类
public class UsbTest { @Test public void test1() throws Exception { //获取目标类对象 UsbFactory factory = new UsbFactory(); //生成代理对象,需要目标对象参与,因为最终目标对象的方法调用交由它自己完成(包括目标方法的一些参数设定)。 UsbSellProxy usbProxy = new UsbSellProxy(factory); //获取动态代理对象 Object obj = Proxy.newProxyInstance(UsbFactory.class.getClassLoader(), UsbFactory.class.getInterfaces(), usbProxy); UsbDirve proxy = (UsbDirve)obj; /* java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的: 如果创建一个对象的新的副本,也就是说他们的初始状态完全一样,但以后可以改变各自的状态, 而互不影响,就需要用到java中对象的复制,如原生的clone()方法。 com.sun.proxy java.lang.ClassCastException:类。 $Proxy7不能强制转换为类think_6.Proxy.UsbFactory (com.sun.proxy。$Proxy7,think_6.Proxy。 UsbFactory在加载器'app'的未命名模块中) */ //通过代理对象执行 proxy.Sell(0); } }
-
-
3、自己编写源码
(工具Idea2020.2(Java Compiler设置的版本为8,且使用本地JDK编译而不是Idea插件编译),JDK版本:1.8 注:在使用JavaCompiler 使用系统调用时要把JDK lib目录下的tools.jar 复制 到jre lib目录下)
注意:接下来的代码展示中都包含了我在做测试代码时的包名,因为在接下来的测试中你会看到创建动态代理类时会与包名有关联。
-
1)定义目标接口:TargetInterface
package think_6.XxProxy; public interface TargetInterface { void targetMethod(); int Sell(int price); }
-
2)定义目标类:TargetClass
package think_6.XxProxy; public class TargetClass implements TargetInterface{ //目标方法1 @Override public void targetMethod() { System.out.println("[TargetClass]:The target method."); } //目标方法2 @Override public int Sell(int price) { System.out.println("[TargetClass]:The price:"+ price +"."); return 0; } }
以上两个类不会再修改,功能增强放在其他地方
-
3)定义InvocationHandler的替代接口:CustomInvocationHandler
package think_6.XxProxy; import java.lang.reflect.Method; public interface CustomInvocationHandler { //需要这个接口只是为提供一个invoke方法用于其实现类能够实现功能增强 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
-
4)TargetProxyClass :CustomInvocationHandler 实现类(用于生成代理类并实现目标方法的功能增强)
package think_6.XxProxy; import java.lang.reflect.Method; public class TargetProxyClass implements CustomInvocationHandler{ //获取被代理目标类对象(即TargetClass对象) private TargetInterface targetInterface = null; public TargetProxyClass() {} public TargetProxyClass(TargetInterface targetInterface) { this.targetInterface = targetInterface; } //在invoke方法中实现目标方法的增强 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; if(method != null) { //利用Method类中的invoke方法执行目标类中的方法 Object dealData = method.invoke(targetInterface, args); //在使用目标方法的基础上实现功能增强 System.out.println("[TargetProxyClass]: targetMethod 功能增强。"); } return null; } }
-
5)Proxy的替代类XxProxy(用于生成动态代理类并加载:$DynamicProxyClass)
package think_6.XxProxy; import javax.tools.JavaCompiler; 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.Method; import java.lang.reflect.Parameter; public class XxProxy { private static String ln = "\r\n"; /** *使用自定义加载器加载DynamicCreateCode生成的动态代理类java文件 */ public static Object newInstance (DynamicProxyClassLoader classLoader, Class<?>[] interfaces, CustomInvocationHandler h) throws IOException { //检查参数是否合法 if (classLoader != null && interfaces != null && h != null) { //获取字符串Java源码 String javaFileStr = DynamicCreateCode(interfaces[0]);//这里只实现了一个接口 String javaFilePath = XxProxy.class.getResource("").getPath() + "$DynamicProxyClass.java"; File javaFile = new File(javaFilePath); System.out.println(javaFilePath); FileWriter writer = new FileWriter(javaFile); writer.write(javaFileStr); writer.flush(); writer.close(); JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); int result = javaCompiler.run(null, null, null, javaFilePath); System.out.println(result == 0 ? "编译成功" : "编译失败"); DynamicProxyClassLoader classLoader1 = new DynamicProxyClassLoader(); try { Class<?> clazz = classLoader.findClass("$DynamicProxyClass"); Constructor instanceConstructor = clazz.getConstructor(CustomInvocationHandler.class); javaFile.delete(); return instanceConstructor.newInstance(h); } catch (Exception e) { e.printStackTrace(); } } return null; } public static String DynamicCreateCode(Class<?> intf) { StringBuffer javaSrc = new StringBuffer(); Method[] methods = null; //获取接口内的所有抽象方法 if(intf != null) { methods = intf.getMethods(); } javaSrc.append(XxProxy.class.getPackage().toString() +";" + ln); javaSrc.append("import java.lang.reflect.Method;" + ln); javaSrc.append("public class $DynamicProxyClass implements " + intf.getName() + "{" + ln); javaSrc.append(" CustomInvocationHandler h;" + ln); //这些静态类型以及下面的switch语句都是为了给重写的接口方法提供合法返回值 javaSrc.append(" static int var1;" + ln); javaSrc.append(" static long var2;" + ln); javaSrc.append(" static short var3;" + ln); javaSrc.append(" static byte var4;" + ln); javaSrc.append(" static float var5 ;" + ln); javaSrc.append(" static double var6;" + ln); javaSrc.append(" static char var7 ;" + ln); javaSrc.append(" static String var8 = \"\";" + ln); javaSrc.append(" static Object var9 = null;" + ln); javaSrc.append(" static Class<?> var10 = null;" + ln); javaSrc.append(" static boolean var11 = true;" + ln); javaSrc.append(" public $DynamicProxyClass(CustomInvocationHandler h) {" + ln); javaSrc.append(" this.h = h;" + ln); javaSrc.append(" }" + ln); //重写所有接口方法 for(int i = 0; i < methods.length; i++) { //获取每个方法的返回值类型 String mehtodReturnType = methods[i].getReturnType().getName(); String setReturnType = ""; switch (mehtodReturnType){ case "int": setReturnType = "var1";break; case "long": setReturnType = "var2";break; case "short": setReturnType = "var3";break; case "byte": setReturnType = "var4";break; case "float": setReturnType = "var5";break; case "double": setReturnType = "var6";break; case "char": setReturnType = "var7";break; case "String": setReturnType = "var8";break; case "Object": setReturnType = "var9";break; case "Class<?>": setReturnType = "var10";break; case "boolean": setReturnType = "var11";break; default:setReturnType=""; } //设置每个方法的参数列表 String methodParamer = ""; //设置CustomInvocationHandler中invoke需要传递的参数 String mrthodParamerName = ""; //获取方法参数类型 String methodParamerType = ""; Parameter[] parameters = methods[i].getParameters(); Class<?>[] parameterTypes = methods[i].getParameterTypes(); for(int j = 0; j < parameters.length; j++){ if(j != parameters.length -1) { methodParamer += parameters[j] + ","; }else{ methodParamer += parameters[j]; } if(j != parameters.length -1) { mrthodParamerName += parameters[j].getName() + ", "; }else{ mrthodParamerName += parameters[j].getName(); } } for(int j = 0; j < parameterTypes.length; j++) { methodParamerType += "," + parameterTypes[j].toString() + ".class"; } javaSrc.append(" public" + " " + mehtodReturnType + " " + methods[i].getName() + "(" + methodParamer + ") {" + ln); //判断方法是否有返回值 if(mehtodReturnType.equals("void")) { javaSrc.append(" try{" + ln); javaSrc.append(" Method m = " + intf.getName() + ".class.getMethod(\"" + methods[i].getName() + "\"" + methodParamerType + ");" + ln); javaSrc.append(" Object[] objs = new Object[]{" + mrthodParamerName + "};" + ln); javaSrc.append(" this.h.invoke(this, m, objs );" + ln); javaSrc.append(" }catch(Throwable e){e.printStackTrace();}" + ln); javaSrc.append(" }" + ln); javaSrc.append(ln); }else{ javaSrc.append(" try{" + ln); javaSrc.append(" Method m = " + intf.getName() + ".class.getMethod(\"" + methods[i].getName() + "\"" + methodParamerType + ");" + ln); javaSrc.append(" Object[] objs = new Object[]{" + mrthodParamerName + "};" + ln); javaSrc.append(" this.h.invoke(this, m, objs );" + ln); javaSrc.append(" }catch(Throwable e){e.printStackTrace();}" + ln); javaSrc.append(" return " + setReturnType + ";" + ln); javaSrc.append(" }" + ln); javaSrc.append(ln); } } javaSrc.append("}" + ln); return javaSrc.toString(); } }
-
6)自定义动态代理类加载器DynamicProxyClassLoder(用于加载动态代理类:$DynamicProxyClass)
package think_6.XxProxy; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; public class DynamicProxyClassLoader extends ClassLoader{ private File classFile = null; //加载器这里写的简单,没什么好说的:1.获取字节码文件;2.重写了ClassLoader类中的findClass() @Override protected Class<?> findClass(String name) throws ClassNotFoundException { File tempClassFile = new File(XxProxy.class.getResource("").getPath() + name + ".class"); //获得类的全限定名 String className = XxProxy.class.getPackage().toString().substring(8) + ".$DynamicProxyClass"; //打印结果信息,无关紧要的一段代码 System.out.println(XxProxy.class.getResource("").getPath() + name + ".class"); System.out.println(className); FileInputStream in = null; ByteArrayOutputStream out = null; if(tempClassFile != null) { classFile = tempClassFile; try { in = new FileInputStream(classFile); out = new ByteArrayOutputStream(); int len = 0; if(in != null) { while((len = in.read()) != -1) { out.write(len); } } tempClassFile.delete(); byte[] classByteArray = out.toByteArray(); return defineClass(className, classByteArray, 0, classByteArray.length); } catch (Exception e) { e.printStackTrace(); } } return null; } }
-
测试类:XxProxyTest
package think_6.XxProxy; import org.junit.Test; import java.io.IOException; public class XxProxyTest { @Test public void test1() throws IOException { //获取目标类对象 TargetClass targetClass = new TargetClass(); //获取代理类 TargetProxyClass h = new TargetProxyClass(targetClass); /*获取动态代理对象(由XxProxy中的newInstance方法创建并返回使用, 其实就是$DynamicProxyClass)*/ Object obj = XxProxy.newInstance(new DynamicProxyClassLoader(), TargetClass.class.getInterfaces(), h); TargetInterface target = (TargetInterface)obj; target.targetMethod(); target.Sell(20); } }
-
生成的动态代理类源码如下:
- $DynamicProxyClass.java(由XxProxy中的newInstance()动态生成)
package think_6.XxProxy;
import java.lang.reflect.Method;
public class $DynamicProxyClass implements think_6.XxProxy.TargetInterface{
CustomInvocationHandler h;
static int var1;
static long var2;
static short var3;
static byte var4;
static float var5 ;
static double var6;
static char var7 ;
static String var8 = "";
static Object var9 = null;
static Class<?> var10 = null;
static boolean var11 = true;
public $DynamicProxyClass(CustomInvocationHandler h) {
this.h = h;
}
public void targetMethod() {
try{
Method m = think_6.XxProxy.TargetInterface.class.getMethod("targetMethod");
Object[] objs = new Object[]{};
this.h.invoke(this, m, objs );
}catch(Throwable e){e.printStackTrace();}
}
public int Sell(int arg0) {
try{
Method m = think_6.XxProxy.TargetInterface.class.getMethod("Sell",int.class);
Object[] objs = new Object[]{arg0};
this.h.invoke(this, m, objs );
}catch(Throwable e){e.printStackTrace();}
return var1;
}
}
- $DynamicProxyClass.class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package think_6.XxProxy;
import java.lang.reflect.Method;
public class $DynamicProxyClass implements TargetInterface {
CustomInvocationHandler h;
static int var1;
static long var2;
static short var3;
static byte var4;
static float var5;
static double var6;
static char var7;
static String var8 = "";
static Object var9 = null;
static Class<?> var10 = null;
static boolean var11 = true;
public $DynamicProxyClass(CustomInvocationHandler var1x) {
this.h = var1x;
}
public void targetMethod() {
try {
Method var1x = TargetInterface.class.getMethod("targetMethod");
Object[] var2x = new Object[0];
this.h.invoke(this, var1x, var2x);
} catch (Throwable var3x) {
var3x.printStackTrace();
}
}
public int Sell(int var1x) {
try {
Method var2x = TargetInterface.class.getMethod("Sell", Integer.TYPE);
Object[] var3x = new Object[]{var1x};
this.h.invoke(this, var2x, var3x);
} catch (Throwable var4x) {
var4x.printStackTrace();
}
return var1;
}
}