为了账号安全,请及时绑定邮箱和手机立即绑定

百万架构师第五课:设计模式:深度分析代理模式

标签:
Java

代理模式

作用:

  • AOP 实现
  • 拦截器
  • 中介
  • 黄牛
  • 媒婆
  • 解耦
  • 专人做专事
  • 自己不想做,又不得不做的事
  • 增强

代理:

  • 静态代理

  • 动态代理

角色

  • 代理角色
  • 被代理的角色(目标对象)

由被代理的角色来做最终的决定

代理角色通常来说会持有被代理角色对象引用(以便于代理角色完成工作之前或者之后能够找到被代理的对象,能够通知被代理对象)

静态代理动态代理的区别:

静态代理:

在代理之前所有的东西都是已知的(人工)

动态代理:

在代理之前,所有的东西都是未知的(自动化,智能化)

总结:

静态代理:

没办法拓展

JKD动态代理的原理


  1. 拿到被代理的对象的引用,并且获取到它的所有接口,反射获取
  2. JDK Proxy 类重新生成一个新的类,同时新的类要实现被代理类的所有实现
  3. 动态的生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
  4. 编译新生成的 Java 代码, .class
  5. 再重新加载到 JVM 中运行

以上这个过程就叫字节码重组, JDK 中有个规范,只要是 $ 开头的一般都是自动生成的。通过反编译工具可以查看源代码。

手写动态代理的原理


  1. 动态生成源代码 .java 文件
  2. JAVA 文件输出到磁盘
  3. 把 .java 文件编译成 .class 文件
  4. 编译生成的 .class 文件加载到 JVM 中,
  5. 返回字节码重组以后的新的代理对象

代理的形象

代理的类图

代码:

静态代理:

Person.class
public interface Person {
    void findLove();

    void zuFangZi();

    void buy();

    void findJob();
}
Son.class
public class Son implements Person {
    public void findLove() {
        // 我没有时间
        // 工作忙
        System.out.println(">>>>>找对象:肤白,貌美,大长腿");
    }

    public void zuFangZi() {

    }

    public void buy() {

    }

    public void findJob() {

    }
}
Father.class
public class Father {
    private Person person;

    // 没办法拓展
    public Father(Person person) {
        this.person = person;
    }

    // 目标对象的引用给拿到
    public void findLove(){
        System.out.println("根据你的要求物色");
        this.person.findLove();
        System.out.println("双方父母是不是同意");
    }
}
StaticProxyTest.class
public class StaticProxyTest {
    public static void main(String[] args) {
        // 只能帮儿子找对象
        // 不能帮表妹、陌生人
        Father father = new Father(new Son());
        father.findLove();
    }
}

JDK:

XieMu.class
public class XieMu implements Person {
    public void findLove(){
        System.out.println(">>>>>高富帅");
        System.out.println(">>>>>身高180");
        System.out.println(">>>>>胸大,6块腹肌");
    }

    public void zuFangZi() {
        System.out.println(">>>>>租房子");
    }

    public void buy() {
        System.out.println(">>>>>买东西");
    }
    public void findJob() {
        System.out.println(">>>>>月薪20K");
        System.out.println(">>>>>找工作");
    }
}
JDK58.class
public class JDK58 implements InvocationHandler {
    // 被代理的对象,把引用给保存下来
    public Person target;

    public Object getInstence(Person target) throws Exception {
        this.target = target;
        Class<? extends Person> clazz = target.getClass();

        // 下半节,深入底层讲字节码是如何重组的。
        // 用来生成一个新的对象(字节码重组来实现)
        //  public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
       return  Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是58,我要给你找工作,现在已经拿到你的简历");
        System.out.println("开始投递");
        method.invoke(this.target,args);
        System.out.println("安排面试");
        return null;
    }
}
JDKMeiPo.class
public class JDKMeiPo implements InvocationHandler {
    // 被代理的对象,把引用给保存下来
    public Person target;

    public Object getInstence(Person target) throws Exception {
        this.target = target;
        Class<? extends Person> clazz = target.getClass();

        // 下半节,深入底层讲字节码是如何重组的。
        // 用来生成一个新的对象(字节码重组来实现)
        //  public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
       return  Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆,我要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");
        method.invoke(this.target,args);
        System.out.println("如果合适的话,就准备办事!");
        return null;
    }
}
JDKProxyTest.class
public class JDKProxyTest {
    public static void main(String[] args) throws Exception {
//        Person person = (Person)new JDKMeiPo().getInstence(new XieMu());
//        person.findLove();
        Person person = (Person)new JDK58().getInstence(new XieMu());
        System.out.println(person.getClass());
        person.findJob();

        // 原理:
//        1. 拿到被代理的对象的引用,并且获取到它的所有接口,反射获取
//        2. JDK Proxy 类重新生成一个新的类,同时新的类要实现被代理类的所有实现
//        3. 动态地生成 Java 代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
//        4. 编译新生成的 Java 代码, .class
//        5. 再重新加载到 JVM 中运行
//        以上这个过程就叫字节码重组


//        JDK 中有个规范,只要是 $ 开头的一般都是自动生成的。
//        通过反编译工具可以查看源代码。
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
        FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class");
        fileOutputStream.write(bytes);
        fileOutputStream.close();
    }
}
$Proxy0.class —JKD反射生成的源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.darian.pattern.staticed.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m4;
    private static Method m2;
    private static Method m3;
    private static Method m5;
    private static Method m0;
    private static Method m6;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void findLove() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void buy() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void findJob() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void zuFangZi() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("com.darian.pattern.staticed.Person").getMethod("findLove");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.darian.pattern.staticed.Person").getMethod("buy");
            m5 = Class.forName("com.darian.pattern.staticed.Person").getMethod("findJob");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m6 = Class.forName("com.darian.pattern.staticed.Person").getMethod("zuFangZi");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

CGLIB

ZhangSan.class
public class ZhangSan {
    public void findLove(){
        System.out.println(">>>>>肤白,貌美,大象腿");
        System.out.println(">>>>>");
    }
}
CGLIBMeiPo.class
public class CGLIBMeiPo implements MethodInterceptor {

    public Object getInstence(Class<?> clazz) throws Exception {
        Enhancer enhancer = new Enhancer();
        // 要把哪个设置为即将生成的新类的父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 业务的增强
        System.out.println("我是媒婆,我要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");
        methodProxy.invokeSuper(o, objects);
        System.out.println("如果合适的话,就办事!");
        return null;
    }
}
CglibTest.class
public class CglibTest {
    public static void main(String[] args) throws Exception {
        ZhangSan zhangSan = (ZhangSan)new CGLIBMeiPo().getInstence(ZhangSan.class);
        zhangSan.findLove();
        System.out.println("----------------------------");
        System.out.println(zhangSan.getClass());
    }
}

Custom:

DarianProxy.class
public class DarianProxy {

    public static String ln = "\r\n";

    public static Object newProxyInstance(DarianClassLoader classLoader, Class<?>[] interfaces, DarianInvocationHandler h) {
        try {
            // 1. 动态生成源代码 .java 文件

            String src = generaterSrc(interfaces);
            // 2. JAVA 文件输出到磁盘
//            String filePath = DarianProxy.class.getResource("").getPath();
//            System.out.println(filePath);
//            File f = new File(  "/$Proxy00.java");
//            FileWriter fw = new FileWriter(f);
//            fw.write(src);
//            fw.flush();
//            fw.close();
            FileOutputStream fileOutputStream = new FileOutputStream("src\\main\\java\\com\\darian\\pattern\\proxy\\custom\\$Proxy0.java");
            fileOutputStream.write(src.getBytes());
            fileOutputStream.close();
            File file = new File("src\\main\\java\\com\\darian\\pattern\\proxy\\custom\\$Proxy0.java");
            
            // 3. 把 .java 文件编译成 .class 文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();

            // 4. 编译生成的 .class 文件加载到 JVM 中,
            Class<?> proxyClass = classLoader.findClass("$Proxy0");
            Constructor<?> constructor = proxyClass.getConstructor(DarianInvocationHandler.class);

            // 5. 返回字节码重组以后的新的代理对象
            constructor.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String generaterSrc(Class<?>[] interfaces) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.darian.pattern.proxy.custom;" + ln);
        sb.append("import com.darian.pattern.staticed.Person;" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);

            sb.append("DarianInvocationHandler h;" + ln);
            sb.append("public $Proxy0(DarianInvocationHandler h){" + ln);
                sb.append("this.h = h;");
            sb.append("}" + ln);
    
            for (Method m : interfaces[0].getMethods()) {
                sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(){" + ln);
                    sb.append("try{" + ln);
                        sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
                        sb.append("this.h.invoke(this, m, null);" + ln);
                    sb.append("}catch(Throwable e){" + ln);
                        sb.append("e.printStackTrace();" + ln);
                    sb.append("}" + ln);
                sb.append("}");
            }
        sb.append("}" + ln);
        return sb.toString();
    }
}
DarianInvocationHandler.class
public interface DarianInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
DarianClassLoader.class
public class DarianClassLoader extends ClassLoader {

    private File classPathFile;

    public DarianClassLoader() {
//            String filePath = DarianClassLoader.class.getResource("").getPath();
//            System.out.println(filePath);
//            File f = new File(  "/$Proxy00.java");
        this.classPathFile = new File("src\\main\\java\\com\\darian\\pattern\\proxy\\custom\\$Proxy0.java");
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = DarianClassLoader.class.getPackage().getName() + "." + name;
        if (classPathFile != null) {
            File classFile = new File(classPathFile, 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 {
                    try {
                        if (null != in) {
                            in.close();
                        }
                        if (null != out) {
                            out.close();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }
}
CustomMeipo.class
public class CustomMeipo implements DarianInvocationHandler {

    // 被代理的对象,把引用给保存下来
    public Person target;

    public Object getInstence(Person target) throws Exception {
        this.target = target;
        Class<? extends Person> clazz = target.getClass();

        // 下半节,深入底层将界字节码是如何重组的。
        // 用来生成一个新的对象(字节码重组来实现)
        //  public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
        return  DarianProxy.newProxyInstance(new DarianClassLoader(), clazz.getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆,我要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");
        System.out.println();
        method.invoke(this.target,args);
        System.out.println();
        System.out.println("如果合适的话,就准备办事!");
        return null;
    }
}
CustomProxyTest.class
public class CustomProxyTest {
    public static void main(String[] args) throws Exception {
//        Person person = (Person)new JDKMeiPo().getInstence(new XieMu());
//        person.findLove();
        Person person = (Person)new CustomMeipo().getInstence(new XieMu());
        System.out.println(person.getClass());
        person.findJob();
    }
}
$Proxy0.class - 编译生成的代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.darian.pattern.proxy.custom;

import com.darian.pattern.staticed.Person;
import java.lang.reflect.Method;

public class $Proxy0 implements Person {
    DarianInvocationHandler h;

    public $Proxy0(DarianInvocationHandler var1) {
        this.h = var1;
    }

    public void findLove() {
        try {
            Method var1 = Person.class.getMethod("findLove");
            this.h.invoke(this, var1, (Object[])null);
        } catch (Throwable var2) {
            var2.printStackTrace();
        }

    }

    public void buy() {
        try {
            Method var1 = Person.class.getMethod("buy");
            this.h.invoke(this, var1, (Object[])null);
        } catch (Throwable var2) {
            var2.printStackTrace();
        }

    }

    public void zuFangZi() {
        try {
            Method var1 = Person.class.getMethod("zuFangZi");
            this.h.invoke(this, var1, (Object[])null);
        } catch (Throwable var2) {
            var2.printStackTrace();
        }

    }

    public void findJob() {
        try {
            Method var1 = Person.class.getMethod("findJob");
            this.h.invoke(this, var1, (Object[])null);
        } catch (Throwable var2) {
            var2.printStackTrace();
        }

    }
}

微信公众号:不止极客

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消