java笔记之动态代理

窗外阴绵绵的,一场疾走的雨在上午短暂地到来又消失。天气有些闷,一个人在卧室无聊时敲几段代码,到是能有一丝心安。

因为疫情影响,至今还未开学,在家自学spring框架,期间遇到了很多问题。

目前被一个动态代理拦住了去路,浪费了不少时间。虽然仍然对其实现原理有许多困惑,但总算是能对其进行照葫芦画瓢地应用了。

话不多说,现在开始谈一谈这段时间对动态代理的理解。

讲动态代理之前,先了解为什么需要动态代理。动态代理能解决相当多的问题,但是按照其代理的进化关系,应该说是为了弥补静态代理的不足,在静态代理的思想上增强其实力。

增强什么实力?就要了解静态代理有什么不足。但是我们得先知道静态代理已经具备怎样的实力。

我最直观的理解,静态代理的目的就是为了对一个对象的行为能力进行增强。先举一个静态代理的例子。

例子:一个行人要出行,他需要先实现拥有出行这个行为的接口,下面是接口代码。

package 静态代理;

public interface Trip {//pedestrian:行人,trip:出行

    void gout();
}

既然是行人出行,就要有人实现出行这个行为,下面是接口的行人实现。

package 静态代理;

public class Pedestrian implements Trip {//pedestrian:行人

    @Override
    public void gout() {
        // TODO Auto-generated method stub
        System.out.println("行人出行……");
    }

}

行人出行有许多方式,可以乘船,坐火车汽车或者飞机等,为了方便,这里让行人乘坐汽车出行,这样就需要让行人坐进汽车内让汽车跑到终点,相当于对行人出行行为的增强,如下代码。

package 静态代理;

public class Car implements Trip {

    private Trip trip;
    
    public Car(Trip trip) {
        this.trip=trip;
    }
    @Override
    public void gout() {
        // TODO Auto-generated method stub
        beforeGout();//出行前增强处理
        trip.gout();
        afterGout();//出行后增强处理
    }
    
    public void beforeGout() {
        System.out.println("出行方式:乘坐汽车出行");
        
    }
    public void afterGout() {
        System.out.println("即将到达目的地,请乘客注意准备下车!");
    }

}

最后来看客户端代码:

package test;

import 静态代理.Car;
import 静态代理.Pedestrian;
import 静态代理.Trip;

public class Test{
    public static void main(String[] args) {
        //第一步,一个人要出行,实例化出一个有出行行为的行人。
        Trip pedestrian=new Pedestrian();
        //第二步,对行人的出行方式进行增强,让行人乘坐汽车出行。
        Trip car=new Car(pedestrian);
        //第三步,行人完成出行行为。
        car.gout();
    }
}

运行结果为:

出行方式:乘坐汽车出行
行人出行……
即将到达目的地,请乘客注意准备下车!

相同的例子,当没有小汽车为其增强出行方式的时候,其运行结果为:

行人出行……

通过对比,我们可以发现,不仅行人出行的动作由汽车完成了,而且汽车还为这个动作做出了详细的情况描述。

这就是静态代理。通过代理方式,让行人自己不想做的事情由来汽车代理来完成,并且提供了增强服务。

静态代理总结

  优点:

    使真实角色处理的业务更加的纯粹,不再关注一些公共的事;

    公共的业务由代理来完成,实现了业务的分工;

    公共业务的扩展变得更加集中和方便。

        缺点:

               类爆炸,也就是当有很多除了行人以外的类需要代理时,就需要重新写很多个代理来满足。这样一个项目就会有大量的类。(感觉大部分教程都并没讲清楚为什么会类爆炸,其实如果只有行人类,出行接口,汽车类,那么再来一个快递类,只要实现了出行接口,同样可以让快递装入汽车并通过实例化的汽车类执行以上操作,而并未需要编写额外的汽车代理类。那么到底为什么会产生类爆炸呢,因为对动态代理理解不够,此处先埋下伏笔。)

        特点:

               编译时代理对象便产生了。

为了解决静态代理的问题,动态代理应运而生。

动态代理主要有两种实现方式。

第一种,基于JDK,需要实现代理的代理委托类必须存在接口。

第二种,基于cglib,需要实现代理的代理委托类必须被继承。

 

这里先来第一种动态代理的解决方案:

预备知识:

T.class.getMethod(String methodName,Class methodType.class...).invoke(Object object,Object[] args)

t.getClass().getMethod(String methodName,Class methodType.class...).invoke(Object object,Object[] args)

t.methodName(...);

其中:T t=new T();

上面两种种执行invoke()方法的结果相同,其中object就是T类的实例化对象,其中的args就是参数的对象数组。

动态代理主要依据Proxy类和InvocationHandler接口实现(都来自reflect反射包里)。

下面开始用动态代理的方式来实现上面的例子:

一个行人要出行,他需要先实现拥有出行这个行为的接口,下面是接口代码:

package 动态代理;

public interface Trip {

    void gout();
}

既然是行人出行,就要有人实现出行这个行为,下面是接口的行人实现。

package 动态代理;

public class Pedestrian implements Trip {

    @Override
    public void gout() {
        // TODO Auto-generated method stub
        System.out.println("行人出行……");
    }

}

与静态代理的不同从这里开始了:

行人出行有许多方式,可以乘船,坐火车汽车或者飞机等,为了方便,这里让行人乘坐汽车出行,这样就需要让行人坐进汽车内让汽车跑到终点,相当于对行人出行行为的增强。

但是,不同于静态代理处理问题的方式,为了统一处理任意的类(包括但不限于行人类),对任意的类的行为的增强,就需要让原本代替行人进行跑到的车(对委托方的类进行增强处理的类)继承一个统一的接口InvocationHandler。(在静态代理中这里的Car类原本继承的接口应该是行人实现的接口,也就是Trip)。

如下代码:

package 动态代理;

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

public class Car implements InvocationHandler {
    
    private Object object;//此处之所以类型为Object,是因为object是代理委托者,相当于行人,然而也可能是一切有代理需求的对象。
    
    public Car(Object object) {
        this.object=object;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        beforeGout();//出行前增强处理
        
        Object result=method.invoke(object, args);//通过反射方式得到行人(代理委托方对象)方法需要执行的操作。
        
        afterGout();//出行后增强处理
        
        return result;//为什么这儿返回的是Object对象result呢,因为动态代理需要处理任意的代理委托方(如行人),此时这里并不能确定代理委托方调用的方法是否有返回值,也不知道返回值是什么类型。所以只有用Object才可以统一处理。
    }
    
    public void beforeGout() {
        System.out.println("出行方式:乘坐汽车出行");
        
    }
    
    public void afterGout() {
        System.out.println("即将到达目的地,请乘客注意准备下车!");
    }

}

嗯,到这里已经的代理写好了,接下来就该测试了,需要注意的是,测试的方法也与静态代理方法有很大差异,下面是客户端测试代码:

package 动态代理;

import java.lang.reflect.Proxy;

public class Test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        //第一步,首先主角出场,也就是实例化代理委托方
        Pedestrian pedestrian=new Pedestrian();
        //第二步,主角的奔驰出场,也就是实例化代理方。
        Car car=new Car(pedestrian);
        //第三步,主角拿到了奔驰的车钥匙,也就是代理委托方与代理方捆绑到了一起。
        Trip p = (Trip)Proxy.newProxyInstance(pedestrian.getClass().getClassLoader(),pedestrian.getClass().getInterfaces(),car);
        //骑上我心爱的小摩托,它永远也不堵车
        p.gout();
    }
}

运行结果:

出行方式:乘坐汽车出行
行人出行……
即将到达目的地,请乘客注意准备下车!

 

至此,动态代理部分算是讲完了。(小朋友你是否有很多问号?)

额,龙岭迷窟更新了,去追更了。基于cglib的动态代理有缘再学,ヾ( ̄▽ ̄)Bye~Bye~。

 补充:

动态代理是对静态代理的扩展与增强,动态代理拥有静态代理上述的全部优点,与静态代理不同的是,动态代理是在运行过程中产生代理类,而静态代理是在编译时已经产生。从方法上看,动态代理应该是一个可以动态地写代码的工具。意思就是当你需要些一个静态代理时,动态代理会为你写好原本应该由你写的且过程重复的代码,然后再去执行这段代码动态生成代理对象。动态代理其实本质是一个工具,就是动态生成静态代理的工具。

posted @ 2020-04-16 22:00  一只小菜菜鸟  阅读(117)  评论(0编辑  收藏  举报