详解 Spring 代理!静态代理、动态代理(代码实例说明)

一、静态代理

1.1 角色分析

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户(Client):访问代理对象的人!

1.2 代理模式的优点和缺点

代理模式的优点:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共业务就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!

代理模式的缺点:

  • 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低

1.3 代码实例 一(客户、中介、房东)

接下来我们就用实例来讲解下具体的用法
以 客户(Client)、中介(Proxy)、房东(Host)为例详细说明

  • 首先定义一个 Rent 接口
public interface Rent {
    
    
	// 定义租房接口
    public void rent();
}

  • 定义房东 Host 类
public class Host implements Rent {
    
    
    @Override
    public void rent() {
    
    
        System.out.println("房东要出租房子!!!");
    }
}
  • 定义中介 Proxy 类
public class Proxy implements Rent{
    
    


    private Host host;

    public Proxy() {
    
    

    }
    public Proxy(Host host) {
    
    
        this.host = host;
    }

    @Override
    public void rent() {
    
    
        seeHouse();
        host.rent();
        hetong();
        fare();
    }


    //看房
    public void seeHouse(){
    
    
        System.out.println("中介带你看房");
    }

    //签合同
    public void hetong(){
    
    
        System.out.println("签租赁合同");
    }

    //收中介费
    public void fare(){
    
    
        System.out.println("收中介费");
    }

}
  • 实现客户 Client 类
public class Client {
    
    
    public static void main(String[] args) {
    
    
        //房东要租房子
        Host host=new Host();

        //代理,中介帮房东租房子,但是呢?代理角色一般会有一些附属操作!
        Proxy proxy = new Proxy(host);

        //你不用面动房东,直接找中介租房即可!
        proxy.rent();
    }
}
  • 查看结果

二、动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口—JDK 动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javasist

需要了解两个类:Proxy(代理),InvocationHandler(调用处理程序)

2.1 InvocationHandler(接口)和Proxy(类)[重要!]

1. InvocationHandler 接口是 proxy 代理实例的调用处理程序实现的一个接口,每一个 proxy 代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的 invoke 方法:

    /**
    * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
    * method:我们所要调用某个对象真实的方法的Method对象
    * args:指代代理对象方法传递的参数
    */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

2. Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。

    public static Object newProxyInstance(ClassLoader loader, 
                                            Class<?>[] interfaces, 
                                            InvocationHandler h)

这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:

  • loader:一个 classloader 对象,定义了由哪个 classloader 对象对生成的代理类进行加载
  • interfaces:一个 interface 对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
  • h:一个 InvocationHandler 对象,表示的是当动态代理对象调用方法的时候会关联到哪一个 InvocationHandler 对象上,并最终由其调用。

直接看,会有点难理解,下面通过两个实例具体了解一下动态代理

2.2 代码实例 一(客户、中介、房东)

  • 定义 Rent 接口
public interface Rent {
    
    

    public void rent();
}

  • 定义房东 Host 对象
public class Host implements Rent {
    
    
    @Override
    public void rent() {
    
    
        System.out.println("房东要出租房子!!!");
    }
}
  • 定义中介 ProxyInvocationHandler 对象
// 自动实现代理类
public class ProxyInvocationHandler implements InvocationHandler {
    
    

    // 被代理的接口
    private Object target;

    public void setRent(Rent rent) {
    
    
        this.target = rent;
    }

    // 生成得到的代理类
    // Proxy类就是用来创建一个代理对象的类
    public Object getProxy(){
    
    
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this );
    }

    // 处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        // 动态代理的本质:就是使用反射机制实现
        Object result = method.invoke(target, args);
        return result;
    }
}
  • 实现客户 Client 对象
public class Client {
    
    
    public static void main(String[] args) {
    
    
        // 真实角色
        Host host = new Host();

        // 代理角色:现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        // 通过调用程序处理角色 -- 向代理角色传入真实角色
        pih.setRent(host);

        // 创建代理对象
        Rent proxy = (Rent) pih.getProxy();

        proxy.rent();
    }
}

2.3 代码实例 二(People、Teacher、WorkHandler)

  • 定义 People 接口
public interface People {
    
    
    public void work();
}
  • 定义 Teacher 对象
public class Teacher implements People{
    
    

    @Override
    public void work() {
    
    
        System.out.println("老师教书育人..");
    }
}
  • 定义 WorkHandler 对象
public class WorkHandler implements InvocationHandler {
    
    

    //代理类中的真实对象
    private Object obj;

    public WorkHandler() {
    
    

    }
    //构造函数,给我们的真实对象赋值
    public WorkHandler(Object obj) {
    
    
        this.obj = obj;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //在真实的对象执行之前我们可以添加自己的操作
        System.out.println("before invoke。。。");
        Object invoke = method.invoke(obj, args);
        //在真实的对象执行之后我们可以添加自己的操作
        System.out.println("after invoke。。。");
        return invoke;

    }
}
  • 实现 Test 对象
public class Test {
    
    
    public static void main(String[] args) {
    
    
        //要代理的真实对象
        People people = new Teacher();
        //代理对象的调用处理程序,我们将要代理的真实对象传入代理对象的调用处理的构造函数中,最终代理对象的调用处理程序会调用真实对象的方法
        InvocationHandler handler = new WorkHandler(people);
        /**
         * 通过Proxy类的newProxyInstance方法创建代理对象,我们来看下方法中的参数
         * 第一个参数:handler.getClass().getClassLoader(),使用handler对象的classloader对象来加载我们的代理对象
         * 第二个参数:people.getClass().getInterfaces(),这里为代理类提供的接口是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
         * 第三个参数:handler,我们将代理对象关联到上面的InvocationHandler对象上
         */
        People proxy = (People) Proxy.newProxyInstance(handler.getClass().getClassLoader(), people.getClass().getInterfaces(), handler);
        //System.out.println(proxy.toString());
        proxy.work();

    }
}

2.4 动态代理的优点

动态代理的优点:

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共业务就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就算对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!

2.5 补充说明

通过上面的讲解和示例动态代理的原理及使用方法,在 Spring 中的两大核心 IOC 和 AOP 中的 AOP (面向切面编程)的思想就是动态代理,在代理类的前面和后面加上不同的切面组成面向切面编程。

上面我们只讲解了 Proxy 中的 newProxyInstance(生成代理类的方法),但是它还有其它的几个方法,我们下面就介绍一下:

  • getInvocationHandler:返回指定代理实例的调用处理程序
  • getProxyClass:给定类加载器和接口数组的代理类的 java.lang.Class 对象。
  • isProxyClass:当且仅当使用 getProxyClass 方法或 newProxyInstance 方法将指定的类动态生成为代理类时,才返回true。
  • newProxyInstance:返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。

猜你喜欢

转载自blog.csdn.net/Kc635908933/article/details/114269251