博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
源码专题之spring设计模式:动态代理
阅读量:7118 次
发布时间:2019-06-28

本文共 8966 字,大约阅读时间需要 29 分钟。

Spring中的AOP底层就是基于动态代理实现的,下面介绍两种动态代理模式。

jdk动态代理

代码实现

满足代理模式应用场景的三个必要条件

  • 两个角色:执行者、被代理对象
  • 注重过程,必须要做,被代理对象没时间做或者不想做(怕羞羞),不专业
  • 执行者必须拿到被代理对象的个人资料(执行者持有被代理对象的引用)

jdk的动态代理通过调用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法,生成目标对象的代理类,interfaces参数为目标对象所实现的全部接口,InvocationHandler的实现类负责在调用方法前后处理自定义逻辑,下面我们以媒婆介绍对象为背景实现:

1 首先是需要找对象的人

public interface Person {    void findLove();        String getSex();    String getName();}---public class XiaoFang implements Person{    private String sex = "女";    private String name = "小芳";        @Override    public void findLove() {        System.out.println("我叫" + this.name + ",性别:" + this.sex + "我找对象的要求是:");        System.out.println("高富帅");        System.out.println("有房有车的");        System.out.println("身高要求180cm以上,体重70kg");    }    public String getSex() {        return sex;    }    public void setSex(String sex) {        this.sex = sex;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

2 小芳不好意思找对象,就要找媒婆来搭桥

public class Meipo implements InvocationHandler {        private Person target; //需要代理的目标对象        //获取被代理人的个人资料    //将目标对象传入进行代理    public Object getInstance(Person target) throws Exception{        this.target = target;        Class clazz = target.getClass();        System.out.println("被代理对象的class是:"+clazz);        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);//返回代理对象    }        @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                System.out.println("开始进行海选...");        System.out.println("------------");        //代理找对象        Object ret = method.invoke(this.target, args);        System.out.println("------------");        System.out.println("如果合适的话,就准备结婚);        return ret;    }}

3 正式开始委托媒婆找对象

public class TestFindLove {    public static void main(String[] args) {        try {            Person obj = (Person)new Meipo().getInstance(new XiaoFang());            System.out.println(obj.getClass());            obj.findLove();        } catch (Exception e) {            e.printStackTrace();        }    }}

手写实现jdk动态代理

这里我们不用jdk提供的reflect api,自己手写实现jdk动态代理,来进一步看一下动态代理的内部实现。

1 需要找对象的人,参照上面的person接口及实现

2 媒婆需要获取被代理人的个人信息,并生成一个替代品(代理对象)

public class MyPorxy {        private static String ln = "\r\n";        public static Object newProxyInstance(MyClassLoader classLoader, Class
[] interfaces, MyInvocationHandler h){ try{ //1、生成源代码 String proxySrc = generateSrc(interfaces[0]); //2、将生成的源代码输出到磁盘,保存为.java文件 String filePath = MyPorxy.class.getResource("").getPath(); File f = new File(filePath + "$Proxy0.java"); FileWriter fw = new FileWriter(f); fw.write(proxySrc); fw.flush(); fw.close(); //3、编译源代码,并且生成.class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null); Iterable iterable = manager.getJavaFileObjects(f); CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable); task.call(); manager.close(); //4.将class文件中的内容,动态加载到JVM中来 //5.返回被代理后的代理对象 Class proxyClass = classLoader.findClass("$Proxy0"); Constructor c = proxyClass.getConstructor(MyInvocationHandler.class); f.delete(); return c.newInstance(h); }catch (Exception e) { e.printStackTrace(); } return null; } private static String generateSrc(Class
interfaces){ StringBuffer src = new StringBuffer(); src.append("package com.lh.reflect;" + ln); src.append("import java.lang.reflect.Method;" + ln); src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln); src.append("MyInvocationHandler h;" + ln); src.append("public $Proxy0(MyInvocationHandler h) {" + ln); src.append("this.h = h;" + ln); src.append("}" + ln); for (Method m : interfaces.getMethods()) { src.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln); src.append("try{" + ln); src.append("Method m = " + interfaces.getName() + ".class.getMethod(\"" +m.getName()+"\",new Class[]{});" + ln); src.append("this.h.invoke(this,m,null);" + ln); src.append("}catch(Throwable e){e.printStackTrace();}" + ln); src.append("}" + ln); } src.append("}"); return src.toString(); }}

3 MyClassLoader代码:

//代码生成、编译、重新动态load到JVMpublic class MyClassLoader extends ClassLoader{    private File baseDir;        public MyClassLoader(){        String basePath = MyClassLoader.class.getResource("").getPath();        this.baseDir = new java.io.File(basePath);    }        @Override    protected Class
findClass(String name) throws ClassNotFoundException { String className = MyClassLoader.class.getPackage().getName() + "." + name; if(baseDir != null){ File classFile = new File(baseDir,name.replaceAll("\\.", "/") + ".class"); if(classFile.exists()){ FileInputStream in = null; ByteArrayOutputStream out = null; try{ in = new FileInputStream(classFile); 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) { e.printStackTrace(); }finally{ if(null != in){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if(null != out){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } classFile.delete(); } } } return null; } }

4 接下来媒婆介绍对象的环节就水到渠成了

public interface MyInvocationHandler {     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}---public class MyMeipo implements MyInvocationHandler {        private Person target;        //获取被代理人的个人资料    public Object getInstance(Person target) throws Exception{        this.target = target;        Class clazz = target.getClass();        System.out.println("被代理对象的class是:"+clazz);        return MyPorxy.newProxyInstance(new MyClassLoader(), clazz.getInterfaces(), this);    }        @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("开始进行海选...");        System.out.println("------------");        Object ret = method.invoke(this.target, args);        System.out.println("------------");        System.out.println("如果合适的话,就准备结婚");        return ret;    }}

总结

jdk动态代理原理:

  • 拿到被代理对象的引用,然后获取它的接口
  • JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
  • 把被代理对象的引用也拿到了
  • 重新动态生成一个class字节码
  • 然后编译

cglib动态代理

在Spring AOP中,通常会用它来生成AopProxy对象。不仅如此,在Hibernate中PO(Persistant Object 持久化对象)字节码的生成工作也要靠它来完成。

代码实现:

1 被代理类

public class GaoFuShuai {    public void findLove(){        System.out.println("肤白貌美大长腿");    }}

2 媒婆(生成代理类,代找对象)

public class GPMeipo implements MethodInterceptor{    //疑问?    //好像并没有持有被代理对象的引用    public Object getInstance(Class clazz) throws Exception{                Enhancer enhancer = new Enhancer();        //把父类设置为谁?        //这一步就是告诉cglib,生成的子类需要继承哪个类        enhancer.setSuperclass(clazz);        //设置回调        enhancer.setCallback(this);                //第一步、生成源代码        //第二步、编译成class文件        //第三步、加载到JVM中,并返回被代理对象        return enhancer.create();    }        //同样是做了字节码重组这样一件事情    //对于使用API的用户来说,是无感知    @Override    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {        System.out.println("开始进行海选...");        System.out.println("------------");        //这个obj的引用是由CGLib给我们new出来的        //cglib new出来以后的对象,是被代理对象的子类(继承了我们自己写的那个类)        //OOP, 在new子类之前,实际上默认先调用了我们super()方法的,        //new了子类的同时,必须先new出来父类,这就相当于是间接的持有了我们父类的引用        //子类重写了父类的所有的方法        //我们改变子类对象的某些属性,是可以间接的操作父类的属性的        proxy.invokeSuper(obj, args);        System.out.println("------------");        System.out.println("如果合适的话,就准备结婚");        return null;    }}

转载地址:http://rysel.baihongyu.com/

你可能感兴趣的文章
Java泛型程序设计
查看>>
Java 中nextLine()方法没有执行直接跳过解决办法
查看>>
重写和重载
查看>>
本表收录的字符的Unicode编码范围为19968至40869
查看>>
PHP多次调用Mysql存储过程报错解决办法
查看>>
MySQL数据库(二) 一一 MySQL管理
查看>>
Js让光标停在输入框input框最后面
查看>>
当面试官问你了不了解defineProperty的时候。。。
查看>>
Unable to make the session state request to the session state server处理方法
查看>>
转发 DDoS攻防战 (一) : 概述
查看>>
redis该怎么用
查看>>
iOS开发,轻松获取根控制器当前控制器的正确方式
查看>>
Mac(不限于)中几个有"内涵"的工具
查看>>
Android 后台杀死及恢复的机制
查看>>
1. JanusGraph的优势
查看>>
supersr--图形上下文的注意点
查看>>
3403: [Usaco2009 Open]Cow Line 直线上的牛
查看>>
【BZOJ】3566: [SHOI2014]概率充电器
查看>>
NSURLsession大文件下载
查看>>
Python 装饰器实例
查看>>