5分钟搞懂Java静态代理与动态代理

Java代理模式

一、代理的概念

​ 代理(Proxy):即代替处理,类似于中间人这一角色。

​ 传统的模式是调用方直接去调用被调用方来达到目的,这样导致的缺点是:耦合度增加。当在两者之间增加了一个代理之后,两者的耦合度会大大降低,每当调用方需要调用某个方法的时候,都回去告知代理并由代理来进行方法的调用。在这里插入图片描述

二、Java代理之——静态代理

使用静态代理的时候,代理对象与目标对象要实现相同的接口,以此来保证两者具有相同的方法。代理对象持有目标对象的句柄。

代码示例:

接口:

public interface Subject {
    void request();
}

目标对象:

//目标对象
public class SubjectImpl implements Subject {
    public void request() {
        System.out.println("----处理请求----");
    }
}

代理对象:

//代理对象
public class SubjectProxy implements Subject{
    //实际的目标对象
    private Subject subject;
    
    public SubjectProxy(SubjectImpl subject){
        this.subject=subject;
    }

    public void request() {
        //预处理
        System.out.println("PreProcess");
        subject.request();//执行目标对象的方法
        //后置处理
        System.out.println("PostProcess");
    }
}

测试类:

public class StaticProxyDemo {
    public static void main(String[] args) {
        //创建实际的目标对象
        Subject subject = new SubjectImpl();

        //代理对象,把目标对象封装到代理对象中,建立代理关系
        SubjectProxy proxy = new SubjectProxy(subject);

        proxy.request();//执行的是代理的方法
    }
}

总结:

优点:结构简单,容易理解。能够在不修改目标对象的基础上,实现功能扩展。

缺点:当目标对象每次添加一个新的方法之后,都需要在代理对象内创建同样的方法,并对其进行包装,当方法多了的时候,代码改动相对来说会比较大且繁琐

三、Java代理之——动态代理

动态代理:对目标对象的方法每次被调用,进行动态拦截。

在这里插入图片描述

​ 在调用代理对象的方法时,实际上是调用目标对象的方法,每次调用代理对象的方法时,都会被拦截然后送到代理处理器里面去,代理处理器都是事先InvocationHandler这个接口的

流程:
在这里插入图片描述

代理处理器:

  • 持有目标对象的句柄
  • 实现InvocationHandler接口
    • 实现Invoke方法
    • 所有的代理对象方法调用,都会被转发到Invoke方法来
    • Invoke的形参method,就是指代理对象方法的调用
    • 在Invoke内部,可以根据method(根据方法、形参这些区别),使用目标对象不同的方法来响应请求

代码案例:

接口:

public interface Subject {
    void request();
}

目标对象:

public class SubjectImpl implements Subject{
    @Override
    public void request() {
        System.out.println("目标对象处理请求!");
    }
}

代理处理器对象:

public class ProxyHandler implements InvocationHandler {
    //实际的目标对象
    private Subject subject;//持有真实的目标对象的句柄

    public ProxyHandler(Subject subject) {
        this.subject = subject;
    }

    //当代理对象调用任何一个方法的时候,此方法都会被调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //输出代理类的名字
        System.out.println(proxy.getClass().getName()); //com.sun.proxy.$Proxy0
        System.out.println("预处理");
        Object result = method.invoke(subject,args);
        System.out.println("后置处理");
        //返回结果
        return result;
    }
}

测试类:

public class StaticProxyDemo {
    public static void main(String[] args) {
        Subject subject = new SubjectImpl();

        SubjectProxy proxy = new SubjectProxy(subject);

        proxy.request();
    }
}

总结:

Java动态代理的核心:

  • 代理对象:不需要自己产生,由Java的Proxy这个类自动的创建出来,只要给定具体的接口,就会自己产生出这个接口的子对象。
  • 代理处理器对象:需要自己定义并且实现Invoke方法

代理对象

  • 根据给定的接口,有Proxy自生成的对象

  • 类型com.sun.proxy.$Proxy0,继承自java.lang.reflect.Proxy

  • 通常和目标对象实现相同的接口(可另实现其他接口)

  • 实现多个接口

    • 接口排序非常重要

    • 当调用多个接口里面的同名方法,则会默认调用第一个接口的方法

代码案例:

接口类:

public interface Cook {
    void doWork();
}
public interface Driver {
    void doWork();
}

目标对象:

public class CookImpl implements Cook{
    @Override
    public void doWork() {
        System.out.println("执行Cook_doWork");
    }
}

代理处理器对象:

public class ProxyHandler implements InvocationHandler {
    private Cook cook;

    public ProxyHandler(Cook cook) {
        this.cook = cook;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理对象类型:"+proxy.getClass().getName());
        System.out.println("调用方法"+method+"参数:"+ Arrays.deepToString(args));
        Object result = method.invoke(cook,args);
        return result;
    }
}

测试类:

public class MultipleInterfaceDemo {
    public static void main(String[] args) throws Exception {
        Cook cook = new CookImpl();
        ClassLoader loader = cook.getClass().getClassLoader();

        ProxyHandler handler = new ProxyHandler(cook);

        /*
        //生成代理类型
        Class<?> proxyClass = Proxy.getProxyClass(loader,
                new Class[]{Cook.class, Driver.class});//给定两个接口

        Object proxy = proxyClass.getConstructor(InvocationHandler.class).
        newInstance(handler);
         */
        Object proxy = Proxy.newProxyInstance(loader,
                new Class[]{Cook.class, Driver.class},
                handler);

        Cook c = (Cook) proxy;
        c.doWork();

        Driver d = (Driver) proxy;
        d.doWork();
    }
}

执行结果:

代理对象类型:com.sun.proxy.$Proxy0
调用方法public abstract void demo.代理.Demo3.Cook.doWork()参数:null
执行Cook_doWork
代理对象类型:com.sun.proxy.$Proxy0
调用方法public abstract void demo.代理.Demo3.Cook.doWork()参数:null
执行Cook_doWork

执行结果:

代理对象类型:com.sun.proxy.$Proxy0
调用方法public abstract void demo.代理.Demo3.Cook.doWork()参数:null
执行Cook_doWork
代理对象类型:com.sun.proxy.$Proxy0
调用方法public abstract void demo.代理.Demo3.Cook.doWork()参数:null
执行Cook_doWork

总结:通过结果可以看到,虽然代理对象转成了Driver并调用Driver的doWork方法,但是因为Cook和Driver两个接口都有同名方法,根据默认的规则,调用的是第一个接口的这个方法。因此,最终调用的是Cook的doWork方法。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生命中有太多不确定

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值