Android 静态代理和动态代理 看这一篇就够了

静态代理和动态代理

根据加载被代理类的时机不同,将代理分为静态代理和动态代理。

  • 编译时就确定了被代理的类是哪一个,那么就可以直接使用静态代理;
  • 运行时才确定被代理的类是哪个,那么可以使用类动态代理。

1、静态代理

public interface Subject {
    public void sayGoodBye();
    public void sayHello(String str);
}

2、 定义被代理类(原来功能类)并实现被代理类的功能逻辑:

public class RealSubject implements Subject {
    @Override
    public void sayGoodBye() {
        System.out.println("RealSubject sayGoodBye");
    }
    @Override
    public void sayHello(String str) {
        System.out.println("RealSubject sayHello  " + str);
    }
}

3、 定义静态代理类(功能增加类),这个代理类也必须要实现和被代理类相同的Subject接口,便于对原有功能的增强:

public class ProxySubject implements Subject {
    private Subject subject;
    public ProxySubject(Subject subject) {
        this.subject = subject;
    }
    @Override
    public void sayGoodBye() {
        //代理类,功能的增强
        System.out.println("ProxySubject sayGoodBye begin");
        //在代理类的方法中 间接访问被代理对象的方法
        subject.sayGoodBye();
        System.out.println("ProxySubject sayGoodBye end");
    }
    @Override
    public void sayHello(String str) {
        //代理类,功能的增强
        System.out.println("ProxySubject sayHello begin");
        //在代理类的方法中 间接访问被代理对象的方法
        subject.sayHello(str);
        System.out.println("ProxySubject sayHello end");
    }
}

4、使用:

public static void main(String[] args) {
    //被代理的对象,某些情况下 我们不希望修改已有的代码,我们采用代理来间接访问
    RealSubject realSubject = new RealSubject();
    //代理类对象
    ProxySubject proxySubject = new ProxySubject(realSubject);
    //调用代理类对象的方法
    proxySubject.sayGoodBye();
    System.out.println("******");
    proxySubject.sayHello("Test");
}

总结: 静态代理(传统代理模)的实现方式比较暴力直接,需要将所有被代理类的所有方法都写一遍,并且一个个的手动转发过去。有点累

二、动态代理

1、在java的动态代理机制中,有两个重要的类或接口
  • 一个是 InvocationHandler(Interface)
  • 另一个则是Proxy(Class)
  • 这一个类和接口是实现我们动态代理所必须用到的
2、 InvocationHandler
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
  • proxy:指代生成的代理对象;
  • method:指代的是我们所要调用真实对象的某个方法的Method对象;
  • args:指代的是调用真实对象某个方法时接受的参数;
  • 每一个代理实类例的invocation handler都要实现InvocationHandler这个接口。并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用
3、 Proxy这个类的 newProxyInstance 这个方法

JDK动态代理需要借助接口来实现,如果我们要代理的对象功能没有抽成任何接口,那么我们就无法通过JDK动态代理的方式来实现。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  • loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
  • interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
  • 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。

第一步:定义一个接口

public interface Subject {
    public void sayGoodBye();
    public void sayHello(String str);
}

第二步:定义真是对象(被代理类):

public class RealSubject implements Subject {
    @Override
    public void sayGoodBye() {
        System.out.println("RealSubject sayGoodBye");
    }
    @Override
    public void sayHello(String str) {
        System.out.println("RealSubject sayHello  " + str);
    }
}

第三步: 定义一个InvocationHandler, 相当于一个代理处理器
SubjectInvocationHandler并不是真正的代理类,而是用于定义代理类需要扩展、增强那些方法功能的类。在invoke函数中,对代理对象的所有方法的调用都被转发至该函数处理。在这里可以灵活的自定义各种你能想到的逻辑。

public class SubjectInvocationHandler implements InvocationHandler {
    //这个就是我们要代理的真实对象
    private Object subject;
    //构造方法,给我们要代理的真实对象赋初值
    public SubjectInvocationHandler(Object subject) {
        this.subject = subject;
    }
    @Override
    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
        //在代理真实对象前我们可以添加一些自己的操作
        System.out.println("before Method invoke");
        System.out.println("Method:" + method);
        //当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
        method.invoke(subject, args);
        //在代理真实对象后我们也可以添加一些自己的操作
        System.out.println("after Method invoke");
        return null;
    }
}

第四步:调用

public static void main(String[] args) {
       //被代理类
       Subject realSubject = new RealSubject();
       //我们要代理哪个类,就将该对象传进去,最后是通过该被代理对象来调用其方法的
       SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);
       //生成代理类
       Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
                                                          realSubject.getClass().getInterfaces(), handler);
       //输出代理类对象
       System.out.println("Proxy : "+ subject.getClass().getName());
       System.out.println("Proxy super : "+ subject.getClass().getSuperclass().getName());
       System.out.println("Proxy interfaces : "+ subject.getClass().getInterfaces()[0].getName());
       //调用代理类sayGoodBye方法
       subject.sayGoodBye();
       System.out.println("--------");
       //调用代理类sayHello方法
       subject.sayHello("Test");
   }

Proxy : com.sun.proxy.$Proxy0
Proxy super : java.lang.reflect.Proxy
Proxy interfaces : com.company.ha.Subject
before Method invoke
Method:public abstract void com.company.ha.Subject.sayGoodBye()
RealSubject sayGoodBye
after Method invoke
--------
before Method invoke
Method:public abstract void com.company.ha.Subject.sayHello(java.lang.String)
RealSubject sayHello  Test
after Method invoke

总结:
与静态代理相比,动态代理具有如下的优点:

  • 代理转发的过程自动化了,实现自动化搬砖;
  • 代理类的代码逻辑和具体业务逻辑解耦,与业务无关;

首先来看看 $Proxy0 这东西,这个东西就是真正的代理类对象,我们定义SubjectInvocationHandler类则是用于添加对代理类的功能扩展!而 $Proxy0类继承java.lang.reflect.Proxy类 并实现Subject接口 ,因此它的类声明方式如下:

同时我们一定要记住,通过 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。

如果想添加功能可以:

@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
    if (method.getName().equals("sayGoodBye")) {//在调用sayGoodBye方法的时候 对返回值进行处理
        int result = (int) method.invoke(subject, args);
        return result + 10;
    } else {//其他方法一律不处理
        return method.invoke(subject, args);
    }
}
发布了51 篇原创文章 · 获赞 78 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_39079048/article/details/98852947