java核心基础之代理机制详解(静态代理、动态代理:JDK、CGlib) - Go语言中文社区

java核心基础之代理机制详解(静态代理、动态代理:JDK、CGlib)


(一)什么是代理?

在生活中经常会遇到代理,比如买房我们是去找中介,而不是自己一栋楼一栋楼去挑选,这里的中介就是代理。代理即通过代理对象访问目标对象,还可以在目标对象基础上增强额外的功能。java的代理分为静态代理和动态代理。静态代理即在代码运行前,代理类就已经存在了。动态代理指代理类不是写在代码中的,而是在运行过程中产生的。

(二)静态代理

静态代理就是在代码运行之前,代理类就已经存在了。通过一个实例来模拟静态代理;:

以租房为例,租房有两种方式,一种是直接找房东去租房,另一种是找租房软件比如自如(不是广告),这种就是通过代理访问目标对象。

首先新建一个租房的接口,只带一个rent方法:

public interface Renting {
    String rent(String name);
}

接着新建一个Person类实现接口,这个类的意思是直接找房东租房,也就是目标对象:

public class Person implements Renting{
    @Override
    public String rent(String name) {
        return "租了"+name;
    }
}

然后新建代理类Ziru,可以直接通过代理类去访问目标对象,我们前面也讲到了代理类不光可以实现访问目标对象,还可以在目标对象基础上增强额外的功能。因此增加一条收取服务类的额外功能。

public class Ziru implements Renting{
    Person person;
    public Ziru(){
        person=new Person();
    }
    @Override
    public String rent(String name) {
        System.out.println("收取手续费");
        return person.rent(name);
    }
}

通过两种方式:使用代理和不使用代理来访问目标对象:

public class run {
    public static void main(String[] args) {
          //不使用代理
//        Person person=new Person();
//        person.rent("碧桂园");
          //使用代理
          Ziru ziru=new Ziru();
          System.out.println(ziru.rent("碧桂园"));
    }
}

得到运行结果。

(三)动态代理

动态代理指代理类不是写在代码中的,而是在运行过程中产生的。java提供了两种实现动态代理的方式,分别是基于Jdk的动态代理和基于Cglib的动态代理。

(3.1) 基于Jdk的Proxy

首先介绍基于Jdk的Proxy,继续使用租房的案例:

其中Renting和Person代码和上面的静态代理相同,新建一个JdkProxy类,这个类是动态代理的核心部分,实现InvocationHandler 接口。InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类invoke,由它实现处理内容。这段代码的另外一个核心是Proxy.newProxyInstance,该方法需要三个参数,目的是运行期间生成代理类,每个参数的功能已经写在了注释中。这段代码的意思就是Proxy 动态产生的代理对象会调用 InvocationHandler 实现类invoke。

public class JdkProxy implements InvocationHandler {
    Renting obj;

    public Renting getProxy(Renting obj){
        this.obj=obj;
        //运行期间创建对象
        Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
        //obj.getClass().getClassLoader()类加载器
        //obj.getClass().getInterfaces() 目标类实现的接口
        //InvocationHandler对象
        return (Renting) proxy;
    }

    //proxy 代理对象
    //method 要实现的方法
    //args 方法的参数
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //拿到目标方法执行结果
        Object invoke = method.invoke(obj, args);
        return invoke;
    }
}

接下来是实现类:

public class run {
    public static void main(String[] args) {
        Person person=new Person();
        JdkProxy jdkProxy=new JdkProxy();
        Renting proxy = jdkProxy.getProxy(person);
        String res = proxy.rent("碧桂园");
        System.out.println(res);
    }
}

当调用proxy.rent()方法时,会自动执行invoke方法,以实现动态代理。

你可以在invoke方法中增加一些自己的代码,比如我在invoke方法中写一条

运行时就会在控制台上打印出来,这也是Spring AOP的核心

JDK动态代理类实现了InvocationHandler接口,重写的invoke方法。

JDK动态代理的基础是反射机制(method.invoke(对象,参数))Proxy.newProxyInstance()

(3.2)基于Cglib的动态代理

通过CGlib也能实现动态代理,在写代码之前导入两个jar包asm-3.3.1,cglib-2.2.2

CGlib动态代理的实现,其中Renting和Person代码和静态代理相同,这里就不再展示。CGlib还可以直接代理类,为了比较,这里依旧使用接口。新建CglibProxy,实现了MethodInterceptor接口,该接口需要实现intercept方法。intercept方法中依旧调用method.invoke()方法。与Jdk不同的是创建代理对象的方式,CGlib中使用Enhance创建代理对象。

public class CglibProxy implements MethodInterceptor {
    Renting obj;
    //通过Enhance动态创建对象
    public Renting getProxy(Renting obj){
        this.obj=obj;
        Enhancer enhancer=new Enhancer();
        //设置父类
        enhancer.setSuperclass(obj.getClass());
        //设置回调方法
        enhancer.setCallback(this);
        return (Renting) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object invoke = method.invoke(obj, objects);
        return invoke;
    }
}

接下来是实现类:

public class run {
    public static void main(String[] args) {
        Person person=new Person();
        CglibProxy cglibProxy=new CglibProxy();
        Renting proxy = cglibProxy.getProxy(person);
        String res = proxy.rent("碧桂园");
        System.out.println(res);
    }
}

得到结果:

(四)总结

1.JDK动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;

2.JDK动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_41973594/article/details/104118797
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢