首页 > 其他分享 >代理模式(Proxy)

代理模式(Proxy)

时间:2023-01-31 22:15:24浏览次数:49  
标签:name 接口 代理 模式 class Person Proxy public

1,代理模式

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。比较合适的理解就是艺人和经纪人的关系。

代理模式又分为静态代理和动态代理。动态代理常用 JDK 代理和 CGLIB 代理。

2,静态代理

由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

1. 定义一个 Person 接口确认具体行为。

//定义 Person 接口,有一个方法 giveMoney()
public interface Person {
    void giveMoney();
}

2. 定义 Student 类,实现具体业务逻辑(被代理类)

public class Student implements Person {

    private String name;
    public Student(String name){
        this.name = name;
    }
    @Override
    public void giveMoney() {
        System.out.println(name + "交学费!");
    }
}

3. 定义代理类 MonitorProxy ,持有一个 Student 类对象,外界可通过这个类来访问 Student 类的方法。

public class MonitorProxy implements Person {

    private Student student;

    public MonitorProxy(Student student){
        this.student = student;
    }

    @Override
    public void giveMoney() {
        System.out.println("我是班长,大家学费交给我了!");
        student.giveMoney();
    }
}

4. 通过 MonitorProxy 来代理执行 Student 操作。

public class ProxyDemoTest {
    public static void main(String[] args) {
        MonitorProxy monitor = new MonitorProxy(new Student("张三"));
        monitor.giveMoney();
    }
}

3,动态代理-JDK

动态代理:在编译期间无法确定需要代理的类。运行期间动态生成代理类。

JDK 动态代理:

在 java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口,通过这个类和这个接口可以生成 JDK 动态代理类和动态代理对象。

1,JDK 动态代理 Demo

1. 定义一个接口规范。

public interface Person {
    void dance();

    void sing();
}

2. 实现接口规范的业务类。

public class Actor implements Person {
    private String name;
    public Actor(String name){
        this.name = name;
    }

    @Override
    public void dance() {
        System.out.println(name + "跳个舞!");
    }

    @Override
    public void sing() {
        System.out.println(name + "唱个歌!");
    }
}

3. 定义处理器,实现 InvocationHandler 接口,invoke 方法定义了代理需要做的事情

public class ProxyHandle<T> implements InvocationHandler {

    T target;
    public ProxyHandle(T target){
        this.target = target;
    }

    /**
     * proxy 表示代理对象
     * method 表示正在执行的方法
     * args 表示调用方法时的参数
     * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行:" + method.getName() + "方法");

        //执行代理方法
        Object invoke = method.invoke(target, args);
        return invoke;
    }
}

4,动态生成代理对象

public class ProxyDemoTest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Person p = new Actor("张三");

        ProxyHandle<Person> personProxyHandle = new ProxyHandle<>(p);

        Person o = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, personProxyHandle);

        o.dance();
        o.sing();

        //获取动态代理类
        //Class<?> proxyClass = Proxy.getProxyClass(null, new Class<?>[]{Person.class});

        //获取到代理类,将其输入到文件种
        byte[] proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", Actor.class.getInterfaces());
        String path = "D:\\gaox\\project\\apz\\demo-base\\src\\main\\java\\com\\demo\\base\\design_partten\\proxy\\code\\com\\demo\\base\\design_partten\\proxy\\dynamics_proxy_jdk\\code\\jdkProxy.class";
        try{
            FileOutputStream fos = new FileOutputStream(path);
            fos.write(proxy0s);
            fos.flush();
        }catch (Exception e){
        }

    }
}

2,JDK 代理原理。

1. 首先有一个被代理的实例(Actor)。这个实例必须实现了一个接口(Person)

2. 定义一个调用处理器,持有被代理实例,实现 InvocationHandler 接口,invoke() 方法是代理具体做的事情

public class ProxyHandle<T> implements InvocationHandler {

    T target;
    public ProxyHandle(T target){
        this.target = target;
    }

    /**
     * proxy 表示代理对象
     * method 表示正在执行的方法
     * args 表示调用方法时的参数
     * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行:" + method.getName() + "方法");

        //执行代理方法
        Object invoke = method.invoke(target, args);
        return invoke;
    }
}

3. 创建一个代理类对象。

/**
 *     loader 一个类加载器
 *     interfaces  一个 class 对象数组,每个元素都是需要实现的接口
 *     hander  一个调用处理器,定义了想使用这个代理干什么事
 */
Person o = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, personProxyHandle);

newProxyInstance 方法内部创建代理类:

 //生成一个代理类
 Class<?> cl = getProxyClass0(loader, intfs);
 ...

 //利用反射使用构造方法返回一个代理类实例
 final Constructor<?> cons = cl.getConstructor(constructorParams);
 return cons.newInstance(new Object[]{h});

此时生成一个默认叫做 $Proxy0 的代理类,拥有 Person 接口的实例方法,访问 $Proxy0 的实例方法,其实是访问执行器的 invoke 方法。

4. 代理类执行对应的方法

通过代理类源码可以看到,其实是执行 ProxyHandle 的 invoke 方法。

3,Proxy 类其他方法。

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler hander)

创建一个代理对象

  1. loader 一个类加载器
  2. interfaces 一个 class 对象数组,每个元素都是需要实现的接口
  3. hander 一个调用处理器,定义了想使用这个代理干什么事

Proxy.isProxyClass(Class<?> cl)

判断一个特定的 class 是否是一个代理类

Proxy.getProxyClass(ClassLoader loader, Class<?>... interfaces)

获取一个动态加载类 class

  1. loader 一个类加载器
  2. interfaces 一个 class 对象数组,每个元素都是需要实现的接口

ProxyGenerator.generateProxyClass("$Proxy0", Actor.class.getInterfaces())

可以获取到代理 class 的字节数组文件。

4,动态代理-CGLIB

CGLIB 一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类与实现 Java 接口

1,CGLIB 动态代理 Demo

1. 定义一个被代理类。(实现不实现接口都无所谓)

public class Actor implements Person {-

    private String name;
    public Actor(){}
    public Actor(String name){
        this.name = name;
    }

    @Override
    public void sing() {
        System.out.println(name + "唱个歌!");
    }

    @Override
    public void dance() {
        System.out.println(name + "跳个舞!");
    }
}

2. 定义一个代理类。

public class MyInterceptor implements MethodInterceptor {

    private Object target;
    public MyInterceptor(Object object){
        this.target = object;
    }

    /**
     * @Param o 代理对象
     * @Param method 被代理对象的方法
     * @Param objects 方法入参
     * @Param methodProxy 代理方法
     * */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib before!");
        Object invoke = methodProxy.invoke(target, objects);
        System.out.println("cglib after!");
        return invoke;
    }
}

3. CGLIB 动态代理

public class ProxyDemoTest {
    public static void main(String[] args) {
        //定义一个被代理实现类
        Actor a = new Actor("藏三");

        //定义方法拦截器
        MyInterceptor interceptor = new MyInterceptor(a);

        // 代理类class文件存入本地磁盘方便我们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\gaox\\project\\apz\\demo-base\\src\\main\\java\\com\\demo\\base\\design_partten\\proxy\\code");

        //通过 CGLIB 动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();

        //设置父类,因为 cglib 是针对指定的类生成一个子类,所以需要父类
        enhancer.setSuperclass(Actor.class);

        //设置回调
        enhancer.setCallback(interceptor);

        //生成代理对象
        Actor actorProxy = (Actor)enhancer.create();
        actorProxy.dance();
        actorProxy.sing();
    }
}

2,CGLIB 实现原理

1. 通过 CGLIB 动态代理获取代理对象的过程

Enhancer enhancer = new Enhancer();

2. 设置父类,因为 cglib 是针对指定的类生成一个子类,所以需要父类

enhancer.setSuperclass(Actor.class);

3. 设置回调,就是对方法的拦截

enhancer.setCallback(interceptor);

4. 生成代理对象

enhancer.create();

5. JDK 代理和 CGLIB 代理的区别

1. 实现原理

  • JDK 代理类是针对被代理类实现的接口做一个实现,针对接口而不是实现类,所以被代理类必须实现接口,使用 JAVA 的反射技术实现。
  • CBLIB 代理类是针对被代理类生成的一个子类,覆盖其中的方法,使用 asm 字节码框架实现。

2. 实现过程

  • JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理,使用条件:实现InvocationHandler + 使用Proxy.newProxyInstance产生代理对象 + 被代理的对象必须要实现接口。
  • CGLib必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法,是一种继承;

针对接口编程的环境下推荐使用JDK的代理

标签:name,接口,代理,模式,class,Person,Proxy,public
From: https://www.cnblogs.com/cnff/p/17009123.html

相关文章