面试官:用最简单的方法给我讲讲什么是静态代理,动态代理

小时候很喜欢看小故事大道理,用通俗易懂的小故事去解释抽象难懂的大道理,并受到启发,了解真谛。今天我们就来试着讲一讲 代理

故事前情提要:老师布置了一道作业,要求同学们做完了交给他。

静态代理

首先,我们先创建一个学生接口,同学们都需要写完交作业

public interface Student {
    void handWork();
}

实现类表示作业写完了要交作业了

public class StudentImpl implements Student {

    private String name;

    public StudentImpl(String name){
        this.name = name;
    }


    public void handWork() {
        System.out.println(name + "交作业");
    }
}

有个同学叫小王,写完了正常交作业

    public static void main(String[] args) {
        StudentImpl student = new StudentImpl("小王");
        student.handWork();
    }

平时也是这么做的,最终打印出来“小王交作业”。

可是有一天,小王忽然有事请假了,他的作业做完了,谁去帮他交呢。没办法,只能委托玩的最好的小伙伴,小李来交作业了,并告诉他,我把交作业的事情全权交于你处理了。你一定不要辜负我的信任呐。

于是乎,小李成了小王的代理

public class StudentProxy implements Student {

    Student student;

    public StudentProxy(StudentImpl impl){
        this.student = impl;
    }

    public void handWork() {
        student.handWork();
    }
}

作业写完了,小王不在,小李被小王委以重任,他就帮小王交了作业

public class StaticProxyTest {

    public static void main(String[] args) {
        StudentImpl student = new StudentImpl("小王");
        StudentProxy studentProxy = new StudentProxy(student);
        studentProxy.handWork();
    }
}

这就是静态代理,所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

小王已经告知了小李需要做什么(交作业),他们都是做作业交作业,倘若有一天小王被老是罚了只有他自己需要交作业,或者小王想喝奶茶,那如果按照上边的逻辑,小李也要受罚,也要被迫喝奶茶了。

JDK动态代理

这次作业是老师罚你自己写的,我才不写呢

public class StudentInvocationHandler implements InvocationHandler {

    Object target;

    public StudentInvocationHandler(Object target){
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" + method.getName() + "方法");
        Object invoke = method.invoke(target, args);
        return invoke;
    }
}

invoke方法可以实现代理逻辑,invoke方法的三个参数含义:

  • proxy:代理对象,就是bind方法生成的对象。
  • method:当前调度的方法
  • args:调度方法的参数

当我们使用了代理对象调度方法后,它就会进入到invoke方法里面。

小李拿到小王的授权,表示什么都能帮你做,只可惜小王只有一个交作业的事情,其实什么喝奶茶,吃辣条我也可以代理

public class ProxyTest {

    public static void main(String[] args) {
        StudentImpl student = new StudentImpl("小王");
        StudentInvocationHandler studentInvocationHandler = new StudentInvocationHandler(student);
        Student studentProxy = (Student)Proxy.newProxyInstance(Student.class.getClassLoader(), new Class[]{Student.class}, studentInvocationHandler);
        studentProxy.handWork();
        //趁机喝小王一杯奶茶,也是可以的
       // studentProxy.drink();
    }
}

建立代理对象和真实对象的关系

Student studentProxy = (Student)Proxy.newProxyInstance(Student.class.getClassLoader(), new Class[]{Student.class}, studentInvocationHandler);

其中newProxyInstance方法包含了3个参数。

  • ClassLoader:类加载器,我们采用了target本身的类加载器。

  • Class<?>[]:是把生成的动态代理对象下挂在哪些接口下,这个写法是放在target实现的接口下。

  • InvocationHandler:是定义实现方法逻辑的代理类,它必须实现InvocationHandler接口的invoke方法,它就是代理逻辑方法的现实方法。

最终打印结果:代理执行handWork方法
小王交作业

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。需要实现java.lang.reflect.InvocationHandler的invoke方法。也需要提供接口方可用JDK动态代理

CGLIB动态代理

如果没有接口,那么JDK动态代理就会抛错JdkProxyExample

需要写一个接口Student,再写一个StudentImpl实现类,小王也很无奈,我没有接口,就一个类,但是我还要你代理我,帮我交作业

public class StudentCglib {
    
    public void handWork(String name) {
        System.out.println(name + "交作业");
    }
}

小李也恭敬不如从命了,毕竟小王说,他请假回来要和我开黑

public class CglibProxy implements MethodInterceptor {

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用真实对象前");
        //CGLIB反射调用真实对象方法
        Object result = methodProxy.invokeSuper(proxy, args);
        System.out.println("调用真实对象后");
        return result;
    }

}

这里采用了CGLIB的加强者Enhander,通过设置超类的方法(setSuperclass),然后通过setCallBack方法设置哪个类为它的代理类。其中,参数为this就意味着是当前对象,那就要求用this这个对象实现接口MethodInterceptor的方法——intercept,然后返回代理对象。
那么此时当前类的intercept方法就是其代理逻辑方法,包含四个参数

  • proxy 代理对象
  • method 方法
  • args 方法参数
  • methodProxy 方法代理

没有接口,我也可以代理

public class CglibTest {

    public static void main(String[] args) {

        //CGLIB enhancer增强类对象
        Enhancer enhancer = new Enhancer();
        //设置增强类型
        enhancer.setSuperclass(StudentCglib.class);
        //定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
        enhancer.setCallback(new CglibProxy());
        //生成并返回代理对象
        StudentCglib proxy = (StudentCglib)enhancer.create();
        proxy.handWork("小王");
    }
}

最终结果: 调用真实对象前
小王交作业
调用真实对象后

最终,小王小李幸福的生活在了一起

JDK动态代理和CGLIB字节码生成的区别?

  1. JDK动态代理只能对实现了接口的类生成代理,而不能针对类
  2. CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final

看完这篇,包你了解JDK动态代理
看完这篇,包你了解CGLIB动态代理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值