设计模式研究

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

2020-07-29  本文已影响0人  Stephenwish
首先静态代理写法和装饰器模式非常像,只是概念分的很细,本质是没有区别,都是用“真正干活”的类去实现方法,代理只是套了一层壳。

第一步抽象接口
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(...)

上一篇下一篇

猜你喜欢

热点阅读