Spring 动态代理 简单讲解

动态代理

java.lang.reflect.Proxy

通过"Proxy"类提供的一个newProxyInstance方法用来创建一个对象的代理对象

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

newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数:

  1. ClassLoader loader 指明生成代理对象使用哪个类装载器 【指定 代理对象 】
  2. Class<?>[] interfaces 指明生成哪个对象的代理对象,通过接口指定. 【指定 被代理对象----使用接口指定,这应就能使用这个接口的所有实现类】
  3. InvocationHandler h 指明产生的这个代理对象要做什么事情。【这是一个接口,通过实现它的 invoke 方法,来指定动态代理要做什么事情】

所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。

注意:在java中规定,要想产生一个对象的代理对象,那么这个对象必须要有一个接口,原因不难分析,如果是指定一个类而不是接口,就只能代理一个类(它的子类),那就一个类,动态代理了个寂寞呀。指定接口就能代理这个接口所有的实现类

出租房子事例:

定义接口——租房子

抽象行为,出租房子

public interface Rent {
    public void rent();
}
定义实现类——房东

这里定义了三个实现类,对应了三个房东

public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("晓军东莞好地段大量房屋出租");
    }
}
public class Host2 implements Rent{
    @Override
    public void rent() {
        System.out.println("凯文大量出租深圳楼盘!");
    }
}
public class Host3 implements Rent{
    @Override
    public void rent() {
        System.out.println("老van黄牛二手转租,凯文晓军 的大房子");
    }
}
实现代理类

代理类干的事情,在这里是 房屋中介 帮房东去 干的事情

// 使用反射导入的一些方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyProxy{
	// 定义一个 出租房子的接口对象
    private Rent rent;
	// setter 方法
    public void setRent(Rent rent) {
        this.rent = rent;
    }
	// 获取一个代理对象
    public Object getProxy() {
        // 使用 Proxy.newProxyInstance(...) 方法获取代理对象
        return Proxy.newProxyInstance( this.getClass().getClassLoader(), rent.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        seeHost();
                        Object result = method.invoke(rent, args);
                        face();
                        return result;
                    }
                }
        );
    }
	// 代理帮房东干的事情
    public void seeHost(){
        System.out.println("看房");
    }
	// 代理帮自己干的事情
    public void face(){
        System.out.println("收中介费");
    }
}
Proxy.newProxyInstance(…) 的 参数解析说明:
  1. this.getClass().getClassLoader() 获取代理类自身的类加载器

  2. rent.getClass().getInterfaces() 获取代理对象的接口(虽然rent本来就是接口引用,但是在使用的时候,会把 接口对象引用 rent 指向它的实现类,所以需要这样子来获取接口)

  3. new InvocationHandler() 这是一个匿名内部类,它没有类名,默认是抽象类和接口 的实现类 (所以匿名内部类在实现调用接口上非常方便)

     @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHost();
        Object result = method.invoke(rent, args);
        face();
        return result;
    }
    

    在匿名内部类中,实现了 接口 InvocationHandler 的 invoke 方法,我们来看一下它的参数

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    

    proxy 被代理对象,method 被代理对象的方法,args 方法的参数列表。

    **看房–seeHost() 和 收中介费 face() **

    seeHost();
    Object result = method.invoke(rent, args);
    face();
    

    因为是内部类,所以可以访问 外部类所有 的 私有的 private 或者 公有 public 的 属性 和 方法。所以在执行被代理对象的方法的前后,可以直接使用方法名,执行代理类内部实现的一些 代理方法

    被代理的方法

    method.invoke(rent, args);
    

    相当于 proxy.method(args); 这里是反射的调用方式。

为什么要用反射来写这个方法呢?而不是直接写?

因为这个重写了 InvocationHandler 接口的 invoke 方法后,调用被代理对象的 所有方法都将被 代理类所代理 ,这个概念有点类似于我们之前使用到的过滤器,即 invoke方法拦截到了 代理对象的 所有方法调用,并像过滤器一样给它前后进行了一些 代理操作。

就像我们之前用过滤器 把所有请求体的 字符编码 都改成 UTF-8 来防止乱码一样。

测试类
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
//        Proxy proxy = new Proxy(host);
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setRent(host);
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}

可能你会问为什么不用 有参构造的 方式 来使代码更加 简洁,因为 在 Spring 中 使用 Setter 注入 会 远比 使用 构造器 注入 多

// 有参构造 注入代理对象 host
ProxyInvocationHandler pih = new ProxyInvocationHandler(host);
// 无参构造 + setter 方法 注入代理对象 host
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setRent(host);

习惯性使用容器的 Setter 注入方法

获取代理对象 proxy

Rent proxy = (Rent) pih.getProxy();

使用 代理对象 proxy 调用 被代理对象 host 的方法

proxy.rent();

通过代理对象执行的方法,都会通过我们在 接口 InvocationHandler 中 实现的 invoke 方法 去调用,结果相当于

proxy.seeHost();
host.rent();
proxy.face();

执行结果截图:

在这里插入图片描述

拓展

在这里插入图片描述

invoke 方法是通过反射去调用的,因此你能在 invoke 方法中通过 反射,类名,方法名,属性名,方法参数的值,方法的注解(注解值),方法参数列表的值 等等,获取所有 反射能获取到的 信息(类的全部信息)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值