JDK 动态代理

我理解和整理的动态代理

动态代理这章学的时候感觉有点复杂,看完狂神的视频还是没完全搞清楚,再结合廖雪峰、bravo1988​等大神的笔记,整理一下我所理解的动态代理,如有不对的地方,请各位大神帮忙指出。

什么是动态代理

要搞清楚动态代理之前,需要先了解静态代理,静态代理的机制是,在目标对象和抽象对象(接口)之间构建一个实现类实现接口,来实现不动源代码而修改功能达到需求的目的。比如现在有房东(目标对象)要出租房屋,中介说帮他出租房租,共同目的都是出租房屋所以他俩是出租房屋接口的实现类,不同的是房东目的很单纯,就出租房,中介就不同了,他能通过引入这个房东的姓名来看出租什么房,在此基础上,可以再加其他要求,比如端茶倒水捶背,最后租客创建实例,调用中介提供的房东’s出租房方法完成租赁,用图来表示是这样的:
静态代理描述

这种架构就很简洁明了,但弊端就是每个目标对象都有一个代理,很浪费代码,而且如果在不更改源代码(接口和目标对象)的情况下,有需求要加,那就麻烦了,每个代理都要改,比如说,中介老大规定所有中介给租客租房前先要给租客鞠个躬,有100个中介,修改代理的代码,加上鞠躬这个动作。

100个也只是假如,如果上万个,哭吧就。所以这时候就需要动态代理了,动态代理就是直接得到代理class对象,不用单独写个代理对象,而是通过某种特殊方法来创建代理对象,等于用独门秘方给你clone出来100个中介,不用一个个去建,而你想要管理这100个中介,管理这个独门秘方就行,这里我看到个有趣的比喻:
接口Class对象是大内太监,里面的方法和字段比做他的一身武艺,但是他没有小DD(构造器),所以不能new实例。一身武艺后继无人。那怎么办呢?
正常途径(implements):写一个类,实现该接口。这个就相当于大街上拉了一个人,认他做干爹。一身武艺传给他,只是比他干爹多了小DD,可以new实例。
非正常途径(动态代理):通过妙手圣医Proxy的克隆大法(Proxy.getProxyClass()),克隆一个Class,但是有小DD。所以这个克隆人Class可以创建实例,也就是代理对象。
来自于知乎 https://www.zhihu.com/question/20794107/answer/658139129

动态代理创建代理对象读取内存过程

一般静态代理从创建代理类,目标类到内存的过程是这样的
静态代理类如何写入内存
动态代理创建代理对象的存取内存的过程是这样的(个人理解所画,如有不对的地方请帮忙指正)
动态代理如何写入内存

写个代码来实现下动态代理

情景:booking系统,bookingAdmin出租书,有人想租书,用动态代理怎么实现?
我主要分接口booking,接口实现类(目标对象)bookingAdmin,动态代理统一类getProxy(其他booking也能用),实例。
booking 接口:

package com.jj.demo02;

//booking接口,有无参和有参两个booking函数
public interface Booking {
    void booking();
    void booking(String bookName);
}

实现类bookAdmin:

package com.jj.demo02;


//booking的实现类,目标类
public class BookAdmin implements Booking{
    @Override
    public void booking() {

    }
    //重写了booking的有参函数
    public void booking(String bookName) {
        System.out.println("预定了"+bookName);
    }

}

通用生成动态代理的类getProxy

package com.jj.demo02;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class getProxy {
    //无参构造函数
    public getProxy() {
    }
    //有参构造函数,target来适用不同的接口和目标对象
    public static Object getProxy(Object target) throws Exception{
        //生成代理类
        Object proxy=Proxy.newProxyInstance(
                target.getClass().getClassLoader(),//加载类
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    //处理代理实例
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //这里调用了目标类的方法
                        Object result=method.invoke(target,args);
                        return null;
                    }
                }
        );
        return proxy;
    }
}


最后实例

import com.jj.demo02.*;

public class MyTest {
    public static void main(String[] args) throws Exception {
        BookAdmin target=new BookAdmin();
        String bookName="红楼梦";
        //实例代理类
        Booking bookingProxy=(Booking) getProxy.getProxy(target);
        bookingProxy.booking(bookName);
    }
}

结果
运行结果

新需求:执行前要先打印下说执行了什么方法

在这里插入图片描述
如果是静态代理,在代理对象上增加一个方法打印出来,那动态代理呢?
如之前所说,直接修改独门秘诀就行,修改getProxy,加打印的方法,代码实现如下

getProxy 类

package com.jj.demo02;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class getProxy {
    //无参构造函数
    public getProxy() {
    }
    //有参构造函数,target来适用不同的接口和目标对象
    public static Object getProxy(Object target) throws Exception{
        //生成代理类
        Object proxy=Proxy.newProxyInstance(
                target.getClass().getClassLoader(),//加载类
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    //处理代理实例
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //新增打印执行了什么方法
                        doLog(method.getName());
                        //这里调用了目标类的方法
                        Object result=method.invoke(target,args);
                        return null;
                    }
                }
        );
        return proxy;
    }
    //打印执行了什么方法的方法
    public static void doLog(String msg){
        System.out.println("执行了"+msg);
    }
}


加了一个doLog的方法,在invoke中调用了一下。所以其他100个1万个代理都可以适用了,方便!

总结

动态代理是通过newProxyInstance生成代理类,invoke进行实例的方法,没有静态代理那么直接单纯,但是对后期修改代码很友好。到这,我才算理解了些动态代理的皮毛,希望我所整理的对同样有困惑的朋友也有帮助,有不对的请大家指教。
最后,bravo1988大神的太监比喻真的很有趣,推荐大家也去看看,链接文中有。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值