自己实现动态代理
其实到这里我们就发现了,代理类其实就是把方法抽象为属性,然后依旧是实现接口,然后重写接口的方法,只不过方法中调用的是InvocationHandler
中的invoke方法。
这里我们可以自己实现编写动态代理。
在手写动态代理前,先理清楚思路。
1.重写InvocationHandler接口
2.重写类加载器,可以进行文件的二进制加载
3.重写Proxy.newProxyInstance方法帮助我们创建.class文件,并且使用类加载器加载,用反射创建实例。
只不过我们的Proxy.newProxyInstance方法采用手动拼接方式生成类文件然后编译生成.class文件,所以具有不通用性,所以我们还是按照之前的卖票来进行编写。
/** * 1.定义卖票接口 */ public interface Tickets { void sell() throws NoSuchMethodException; }
/** * 2、12306类 */ public class Demo12306 implements Tickets { @Override public void sell() { System.out.println("卖票"); } }
/** * 3、重写InvocationHandler接口 * 这里不把数组作为参数,方便拼接java文件 */ public interface MyInvocationHandler { Object invoke(Object proxy, Method method, Object args); }
/** * 4、编写自定义类加载器 */ public class MyClassLoader extends ClassLoader { private File dir; //需要加载的.class文件路径 public MyClassLoader(String path) { dir = new File(path); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { /** * 1.获取到该.class文件 * 2.写成流 * 3.使用defineClass加载类 */ File clazzFile = new File(dir, name + ".class"); if (clazzFile.exists()) { try { FileInputStream input = new FileInputStream(clazzFile); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = input.read(buffer)) != -1) { baos.write(buffer, 0, len); } return defineClass("com.demo." + name, baos.toByteArray(), 0, baos.size()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return super.findClass(name); } }
/** * 自定义的动态代理Proxy类 */ public class MyProxy { /** * 这里第二个参数没有做成数组是因为偷懒,也就是只能实现一个接口,有兴趣的可以改为数组 */ public static Object newProxyInstance(MyClassLoader loader, Class interfaces, MyInvocationHandler handler) { try { //把java文件写入到本项目com.emo包下 String proxyClass = getClassString(interfaces); String filePathName = "src/com/demo/$Proxy0.java"; File file = new File(filePathName); FileWriter fw = new FileWriter(file); fw.write(proxyClass); fw.flush(); fw.close(); //编译上面生成的java文件生成.class JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName); CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects); task.call(); fileManager.close(); //通过类加载器加载.class文件,使用反射创建对象 Class<?> proxy0Clazz = loader.findClass("$Proxy0"); Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class); Object instance = constructor.newInstance(handler); return instance; } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } static String rt = "\r\n"; /** * 手动拼接代理类 */ private static String getClassString(Class interfaces) { //01.使用拼凑字符串的方式将内存中的代理类拼出来 return "package com.demo;" + rt + "import java.lang.reflect.Method;" + rt + "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt + "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}" + getMethodString(interfaces) + rt + "}"; } private static String getMethodString(Class interfaces) { String proxyMe = ""; for (Method method : interfaces.getMethods()) { proxyMe += "public void " + method.getName() + "() throws NoSuchMethodException {" + rt + "Method md = " + interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt + "this.h.invoke(this,md,null);" + rt + "}" + rt; } return proxyMe; } }
public class MeiTuan implements MyInvocationHandler { private Tickets tickets; MeiTuan(Tickets tickets){ this.tickets = tickets; } @Override public Object invoke(Object proxy, Method method, Object args) { System.out.println("加速包开始抢票"); try { tickets.sell(); } catch (NoSuchMethodException e) { e.printStackTrace(); } System.out.println("加速包抢票成功"); return null; } }
public class Test { public static void main(String[] args) throws NoSuchMethodException { Tickets demo12306 = new Demo12306(); MeiTuan proxy = new MeiTuan(demo12306); Tickets meiTuan = (Tickets) MyProxy.newProxyInstance(new MyClassLoader("src/com/demo"), com.demo.Tickets.class, proxy); /** * 打印结果: * 加速包开始抢票 * 卖票 * 加速包抢票成功 */ meiTuan.sell(); } }
这里发现动态代理生效了,这里只是给大家做个小例子,实际过程中代码编写不是很完美不用纠结,体会过程最重要。
这个时候在我们的包下也生成了java类和.class文件
package com.demo; import java.lang.reflect.Method; public class $Proxy0 implements com.demo.Tickets { MyInvocationHandler h; public $Proxy0(MyInvocationHandler h) { this.h = h; } public void sell() throws NoSuchMethodException { Method md = com.demo.Tickets.class.getMethod("sell", new Class[]{}); this.h.invoke(this, md, null); } }
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.demo; import java.lang.reflect.Method; public class $Proxy0 implements Tickets { MyInvocationHandler h; public $Proxy0(MyInvocationHandler var1) { this.h = var1; } public void sell() throws NoSuchMethodException { Method var1 = Tickets.class.getMethod("sell"); this.h.invoke(this, var1, (Object)null); } }
cglib实现动态代理
jdk是通过接口实现代理,而cglib则是通过继承实现代理,对于final类无法代理
底层是采用字节码方式实现的
还是使用原来的例子
maven项目添加依赖
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
public abstract class Tickets { abstract void sell(); }
public class Demo12306 extends Tickets { @Override public void sell() { System.out.println("卖票"); } }
public class CglibProxy implements MethodInterceptor { private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz) { //设置需要创建子类的类 enhancer.setSuperclass(clazz); enhancer.setCallback(this); //通过字节码技术动态创建子类实例 return enhancer.create(); } //实现MethodInterceptor接口方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("加速包开始抢票"); //通过代理类调用父类中的方法 Object result = proxy.invokeSuper(obj, args); System.out.println("加速包抢票成功"); return result; } }
public class Test { public static void main(String[] args) { CglibProxy proxy = new CglibProxy(); Tickets tickets = (Tickets) proxy.getProxy(Demo12306.class); tickets.sell(); } }
最终结果和jdk的动态代理结果一致
参考博客:
https://blog.csdn.net/tanggao1314/article/details/50450459
https://www.cnblogs.com/9513-/p/8432276.html