Discuss / Java / 动态代理的一些思考与笔记

动态代理的一些思考与笔记

Topic source

末子网络

#1 Created at ... [Delete] [Delete and Lock User]

可能有人会像我一样,看到这节的时候出现的感觉有两种

1、这一节没什么特别,我看完了

2、这一节说的东西不明觉厉,但是感觉(暂时、或大多数情况下)用不上,了解下就行

然后紧接着往后看,注解(或者其它章节,因为我是初学,再往后我还没看),发现愈发不能愉快的玩耍了,兜兜转转,又回到这里,以期更好的理解一种新的概念=》代理模式

在经历一番混沌到朦胧再到目前有点感觉,我把自己的理解,尝试写出来做个笔记,希望被更多人看到,指出我理解的错误。

为了搞清楚动态代理的概念,我搜素了很多,大概的搜素路劲从“动态代理”、“静态代理”、“静态代理的意义”、“动态代理的运用”、“代理模式” … 信息量很大,比这里讲解要更多、更全,因为检索的内容大多有针对性,而我们在看的教程是一个渐进式的,不可能把每个点都用巨大篇幅,说到根源说的通透,这样咱也看不下去啊。

我自己大概总结:代理,是关于编程思想、面向对象、甚至开发习惯的一个综合性知识,在此之前,要理解面向对象(重载、覆写、继承、接口、内部类)以及反射的原理,有了这些知识后,再尝试梳理,或许才能会更好的理解它。

引用如下几种例子,来说明代理问题

关于代理模式

猪八戒在高老庄挑逗高翠兰,然而高翠兰是孙悟空变的,这个时候孙悟空就是高翠兰代理,整个过程是,把高翠兰的模样、身段 … 抽象出来,高翠兰和孙悟空都实现了它

但是基于我的段位,我觉得这样的例子不是很恰当,因为孙悟空代理后,就嫁给猪八戒的这个方法,完全是破坏了封装性了(改写了,不再是嫁这件事了 …)。而我认为,代理应该是为了方法增强(注解也是,这是为什么我看到注解又折回来的原因),而不是改变方法本身。就是说,我们可以利用代理让本来该发生的事情不发生(return null),也可以让在该发生的事情之前或之后,做一些额外的事情,但我们不能即让这件事情发生了,又强行改变事情过程。

关于动态代理

楼下这个例子(学生、区长儿子、市长儿子…)已经很棒了,反正在此之前,我是举不出来这么厉害的栗子 … 我也在评论里摇旗呐喊了 …

也是和上面一样的道理,假如我们只是 System.out.println("我可以吃香喝辣 … ") 并不能真正体会到代理的厉害,因为毕竟都是嘴上说说(打印到屏幕)嘛,没有动手 … 我想可以这样改进下,有小学生若干,他们都实现了一个 小学生日常 的接口(吃饭、写作 …),现在校长开会,我们校方应该把所有孩子日常行为,都记录下来,日后好向他们家长要钱,同时,这个班里有几名领导的孩子,区长家的孩子吃饭时候加鸡腿、市长儿子吃饭的时候加汉堡包,省长儿子更牛逼,吃汉堡包还要给瓶可乐,并且他们的儿子写作的时候要加分 …

这就不一样了,不是说说而已了,而是要实实在在加鸡腿 … 好我们来干这件事,再说一下我的理解,并不是利用代理,去改写方法本身,如果是改写方法本身,那就是多态就行了

interface Behaviour { // 定义小学生日常行为接口
    void eat();    
    void write(String title);
}
abstract class Student implements Behaviour { // 抽象类 实现小学生日常行为 这里本身没有实现 交给它的子类实现
    private String name = ""; // 学生姓名
    private String title = ""; // 作文题目
    private int score = 0; // 作文得分
    private Foods foods = Foods.FRI; // 今天吃什么 枚举类
    public Student() { // 构造方法,没有传姓名生成随机姓名
        Integer n = (int) ((Math.random() * 9) + 1) * 100000;
        setName(n.toString());
    };
    public Student(String name) {
        setName(name);
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
    public void setScore(int score) {
        this.score = score;
    }
    public int getScore() {
        return this.score;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getTitle() {
        return this.title;
    }
    public Foods getFoods() {
        return this.foods;
    }
}

普通学生

class Ordinary extends Student { // 继承自抽象的学生类
    public Ordinary() {}
    public Ordinary(String name) {
        super(name);
    }
    @Override
    public void write(String title) {
        setTitle(title);
        // 给作文打个分
        int[] scores = {5, 10, 15, 20};
        Random random = new Random();
        setScore(scores[random.nextInt(3) + 0]);
    }
    @Override
    public void eat() {
        System.out.println("今天" + getFoods().day + ", 我吃了" + getFoods());
    }
    @Override
    public String toString() {
        return "写了一篇作文《" + getTitle() + "》,得分" + getScore();
    }
}

区长的儿子

class District extends Student {
    public District() {}
    public District(String name) {
        super(name);
    }
    @Override
    public void write(String title) {
        setTitle(title);
        // 给作文打个分
        int[] scores = {5, 10, 15, 20};
        Random random = new Random();
        // 区长儿子 领导意思是加5分
        int realScore = scores[random.nextInt(3) + 0];
        if (realScore <= 15) { // 满分20
            System.out.println(getName() + "是区长儿子,领导让加5分");
            realScore += 5;
        }
        setScore(realScore);
    }
    @Override
    public void eat() {
        // 区长儿子 巨喜欢吃鸡腿
        String menu = getFoods().toString();
        menu = menu + ",鸡腿";
        System.out.println("今天" + getFoods().day + ", 我吃了" + menu);
    }
    @Override
    public String toString() {
        return "写了一篇作文《" + getTitle() + "》,得分" + getScore();
    }
}

好了,目前为止,我们实现了校长开会定下来的指导方针:区长家的孩子吃饭时候加鸡腿 … 并且他们的儿子写作的时候要加分

public static void main(String[] params) {
    Student[] students = {
            new Ordinary("小明"),
            new District("大明")
    };
    for (Student student: students) {
        student.eat();
        student.write("作文题目都一样");
        System.out.println(student);
    }
}

结果如下:

今天星期五, 我吃了沙拉大白菜
写了一篇作文《作文题目都一样》,得分5
今天星期五, 我吃了沙拉大白菜,鸡腿
大明是区长儿子,领导让加5分
写了一篇作文《作文题目都一样》,得分15

接下来,我们要实现校长的另一个英明决定:把所有孩子日常行为,都记录下来,日后好向他们家长要钱

首先,异议的地方来了,楼下作者的意思是,通过代理,解决未来再出个常委的儿子、司令的儿子 … ,而我想的是,这不能够,这还是多态,有一万个不同领导的孩子,就得有一万个多态去实现,代理可以为这一万个孩子做一件相同的事(可以提条件),但不能代做任何事。你看,这两句的区别就是 代,但是此代非彼代,代理 != 代做,这是重点!MD,优秀,我怎么这么棒 …

好了,假如有一万个孩子,那就有一万个实现类(多态),如果我要为孩子们写记录,就得在一万个实现类里每个接口方法前后,去写记录代码 ~ 这个时候作为攻城狮有两个选择,砸电脑回家刷抖音,或者想办法少些代码,攻城狮们永不为奴 !!! 开始他的表演 ~

public static void main(String[] params) {
    District district = new District("皇小明"); // 区长儿子 皇小明
    Behaviour districtProxy = (Behaviour) getProxy(district); // 这里为什么拿到的是 Behaviour 类型,因为接口不能实例化,Proxy.newProxyInstance 顾名思义是生成代理接口对象
    districtProxy.eat(); // 区长儿子吃饭饭
    districtProxy.write("爸爸的爸爸学代理"); // 区长儿子写作文
    System.out.println(districtProxy);
}

public static Object getProxy(final Object target) {
    Object proxy = Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 代理目标类的 class 的 class 生成器
            target.getClass().getSuperclass().getInterfaces(), // 代理类要实现的接口 本例中是 目标类的父类的接口
            new InvocationHandler() { // 中转反射器,代理类执行方法,到这里,中转反射到目标类上,如果感觉名字怪怪的那就对了,我自己瞎起的,不在意这些细节
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("孩子们" + method.getName() + "了...");
                    Object result = method.invoke(target, args);
                    // System.out.println(result);
                    System.out.println("孩子们" + method.getName() + "结束了...");
                    return result;
                }
            }
    );
    return proxy;
}
孩子们开始eat了...
今天星期五, 我吃了沙拉大白菜,鸡腿
孩子们eat完了...
孩子们开始write了...
皇小明是区长儿子,领导让加5分
孩子们write完了...

完 ~

哈哈写的很生动,很清晰

Awdcuhhk

#3 Created at ... [Delete] [Delete and Lock User]

CY猪八戒娶媳妇

Awdcuhhk

#4 Created at ... [Delete] [Delete and Lock User]
public enum Foods {    MON("星期一", "白切鸡"), TUE("星期二", "白切鸡"), WED("星期三", "白切鸡"), THU("星期四", "白切鸡"), FRI("星期五", "白切鸡"), SAT("星期六", "白切鸡"), SUN("星期日", "白切鸡");    public final String day;    private final String chinese;    private Foods(String day, String chinese) {        this.day = day;        this.chinese = chinese;    }    @Override    public String toString() {        return this.chinese;    }}

补充个Foods的枚举类

Awdcuhhk

#5 Created at ... [Delete] [Delete and Lock User]
public enum Foods {
    MON("星期一", "白切鸡"), TUE("星期二", "白切鸡"), WED("星期三", "白切鸡"), THU("星期四", "白切鸡"), FRI("星期五", "白切鸡"), SAT("星期六", "白切鸡"), SUN("星期日", "白切鸡");    
    
    public final String day;    

    private final String chinese;    

    private Foods(String day, String chinese) {
        this.day = day;        
        this.chinese = chinese;      
    }

    @Override    
    public String toString() {
        return this.chinese;    
    }

}

格式咋变了?

ericwang_1992

#6 Created at ... [Delete] [Delete and Lock User]
District district = new District("黄晓明");
Behaviour districtProxy = (Behaviour) getProxy(district);
districtProxy.eat();districtProxy.write("我的区长爸爸");
System.out.println(districtProxy);
孩子们eat了。。。
今天我吃了大白菜,鸡腿
孩子们eat结束了。。。
孩子们write了。。。
黄晓明是区长儿子,领导让加5分
孩子们write结束了。。。
孩子们toString了。。。
孩子们toString结束了。。。
写了一篇作文《我的区长爸爸》,得分10

为撒我这还有个toString啊,Behaviour里面定义是一样的,只有两个方法。

看了兄弟写的总结,自己测试了一下动态代理,瞬间明白了这东西是做什么用了,感谢。Spring AOP切片的底层实现就是动态代理在做了,动态代理只是对目标函数做了扩展,核心处理逻辑肯定是目标函数去做啦。


  • 1

Reply