美文网首页设计模式研究
一步一步从静态代理写到动态代理

一步一步从静态代理写到动态代理

作者: Stephenwish | 来源:发表于2020-07-29 17:15 被阅读0次
    首先静态代理写法和装饰器模式非常像,只是概念分的很细,本质是没有区别,都是用“真正干活”的类去实现方法,代理只是套了一层壳。

    第一步抽象接口
    public interface Movable {
        void move();
    }
    

    第二步实现抽象接口(真正干活的类)

    public class Subject implements Movable{
        @Override
        public void move() {
            System.err.println("实现移动的具体实例Subject");
        }
    }
    

    第三步,新建一个类,携带具体干活的Subject类,(用别人的手去抓蛇)

    public class AProxySubject {
        private  Subject  subject;
    
        public AProxySubject(Subject subject) {
            this.subject = subject;
        }
    
        @Override
        public void move() {
            System.err.println("proxy log before AAA");
            subject.move();
            System.err.println("proxy log before AAA");
        }
    }
    

    第四步,写一个场景类测试

    public class Client {
        public static void main(String[] args) {
                 aProxySubject= new AProxySubject(
                            new Subject()
                    );
           
            aProxySubject.move();
        }
    }
    

    \color{red}\bigstar新增需求:现在想在旧的基础上再套一层代理,或者套N层代理即场景类如下:

    public class Client {
        public static void main(String[] args) {
    
            BProxySubject bProxySubject =
                    new BProxySubject(
                        new AProxySubject(
                            new Subject()
                    )
            );
            bProxySubject.move();
        }
    }
    

    这时候代理类可以把旧的Subject 换成Subject 的父类 Movable,这时候变为:

    public class AProxySubject {
        private Movable subject;
    
        public AProxySubject(Movable subject) {
            this.subject = subject;
        }
    
        @Override
        public void move() {
            System.err.println("proxy log before AAA");
            subject.move();
            System.err.println("proxy log before AAA");
        }
    }
    

    看构造器知道,也就是构建代理类,需要准备Movable subject 这个材料,那么我自己也是可以变成这块的材料啊,所以在改下(自己也实现Movable)

    public class AProxySubject implements Movable{
        private  Movable subject;
    
        public AProxySubject(Movable subject) {
            this.subject = subject;
        }
    
        @Override
        public void move() {
            System.err.println("proxy log before AAA");
            subject.move();
            System.err.println("proxy log before AAA");
        }
    }
    

    然后同样造出B代理类

    public class BProxySubject implements Movable {
        private Movable subject;
    
        public BProxySubject(Movable subject) {
            this.subject = subject;
        }
    
        @Override
        public void move() {
            System.err.println("proxy log before BBB");
            subject.move();
            System.err.println("proxy log before BBB");
        }
    }
    

    最后验证Client:

    public class Client {
        public static void main(String[] args) {
    
            BProxySubject bProxySubject =
                    new BProxySubject(
                        new AProxySubject(
                            new Subject()
                    )
            );
            bProxySubject.move();
        }
    }
    

    \color{red}\bigstar新增需求:现在把上面实现的静态代理,换另一种比较灵活写法,用Proxy来写

    第一步,照常提供接口,和具体干活类
    public interface Movable {
        void move();
    }
    
    
    public class Tank implements   Movable{
        @Override
        public void move() {
            System.err.println("我是具体实例TAnk");
        }
    }
    
    第二步,提供场景测试类
    public class Client {
        public static void main(String[] args) {
        //首先开启proxy类生成,动态代理通过ASM会生成一个类,一般来讲,我们写  JAVA,写完编译后就是一个class类,动态代理是电脑帮我们写了一个类
     System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    
    }
    
    

    (上面代码完整后,会出现如下图目录和文件)


    image.png
    image.png
    接着继续完善上面代码
    public class Client {
        public static void main(String[] args) {
        //首先开启proxy类生成,动态代理通过ASM会生成一个类,一般来讲,我们写  JAVA,写完编译后就是一个class类,动态代理是电脑帮我们写了一个类
     System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
       Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(), new Class[]{Movable.class}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //第一个参数是生成的代理对象,也就是m,基本没用上
                    System.err.println("代理前");
                    //持有具体类
                    //具体类返回什么类型就返回什么类型
                    Object o = method.invoke(new Tank(), args);
                    System.err.println("代理后");
                    return o;
                }
            });
    
    //准备完成后测试move
            m.move();
    }
    

    \color{red}\spadesuit 这里重点讲Proxy.newProxyInstance 这个方法它有三个参数:

    第一个参数是类加载器,他是把生成的$proxy0.class 加载到内存,不然你怎么使用呢,一般选择和被代理类相同的加载器就可以了。

    第二个参数指定生成的proxy0.class 需要实现的接口

    第三指定proxy0.class 内部使用的InvocationHandler的具体实现方法,因为他以后要使用这个方法。


    \color{blue}\spadesuit 这里重点讲InvocationHandler,它重写了一个方法:
    public Object invoke(Object proxy, Method method, Object[] args),

    第一个参数他是生成代理对象,也就是$proxy0.class,基本没什么用

    第二个参数Method method,也就是接口方法,他是没有实现的,代理才不会自己干活,看上面程序可以知道最后还是要放一个真正干活的类进去,这个方法会返回一个对象Object o, 这个是和真正干活对象返回类型一致,是什么他就是什么类型,这里是Void

    第三个参数是方法参数,具体干活的类是什么他就是什么


    \color{green}\spadesuit Proxy.newProxyInstance 前面有个强转Movable,因为你看上面的$proxy0,他本身就实现了Movable这个接口,当然可以强转了

    image.png

    上面的h.invoke(),就是我们在场景类Client匿名函数实现的invoke

    \color{red}\bigstar新增需求:假设我没有接口我该怎么实现,我就只有一个普通类这时候就用CGLIB

    非常普通的一个类(不能在普通了)
    public class Tank {
        public void move(){
            System.err.println("mov mov mov");
        }
    }
    
    新增场景类(我把导入的包也贴出来)
    package cn.xqrcloud.mypattern.代理模式.动态代理.v2;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * 🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌道阻且长,行则将至🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌
     * 🍁 Program: mypattern
     * 🍁 Description
     * 🍁 Author: Stephen
     * 🍁 Create: 2020-07-29 00:31
     * 🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌行而不辍,未来可期🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌🐌
     **/
    public class Client {
        public static void main(String[] args) {
                Enhancer enhancer=new Enhancer();
                enhancer.setSuperclass(Tank.class);//设置父类
            //enhancer.setCallback(new MyInvocationHandler());//相当于InvocationHandler
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    //第一个参数继承Tank的类
                    System.err.println("开始之前");
                    Object result = method.invoke(new Tank(), objects);
    
                    //  Object result = methodProxy.invokeSuper(o, objects);
                    System.err.println("开始之后");
                    return result;
                }
            });
            Tank tankimpl = ((Tank) enhancer.create());
            tankimpl.move();
    
        }
    }
    
    第一步新增一个Enhancer 增强类,接着我把Tank 当接口或者父类(接口启示相当于父类了),接着实例一个以tank为接口的代理类tankimpl ,执行move方法,

    而且move方法里面要用到类似InvocationHandler的东西,这里叫MethodInterceptor
    所以在创建具体的tankimpl 之前,要把这个材料准备好,执行 enhancer.setCallback(...)

    相关文章

      网友评论

        本文标题:一步一步从静态代理写到动态代理

        本文链接:https://www.haomeiwen.com/subject/dxxkrktx.html