JDK动态代理

一.什么是代理

代理其实就是一种常用的设计模式。这种通过,工程师编辑代理类代码zx nkl,.;/6yujhnm 089p-/’]=[’]’
.m,实现代理模式;在编译期就生成了代理类的方式通常也被称作静态代理。

  • 代理模式的定义:
    为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
/**
 * 猫咪接口
 */
public interface Cat {
    void eat();
    void drink();
    void wc();
}

/**
 * 猫咪实体类:咪咪
 * 实现猫咪接口
 */
class MiMi implements Cat {
    @Override
    public void eat() {
        System.out.println("咪咪吃饭");
    }
    @Override
    public void drink() {
        System.out.println("咪咪喝水");
    }
    @Override
    public void wc() {
        System.out.println("咪咪上厕所");
    }
}

/**
 * 代理类:铲屎官
 * 负责照顾咪咪
 */
class ProxyCat implements Cat {

    MiMi mimi;

    public ProxyCat() {
        this.mimi = new MiMi();
        System.out.println("实例化铲屎官");
    }

    @Override
    public void eat() {
        System.out.println("给咪咪倒猫粮");
        mimi.eat();
    }
    @Override
    public void drink() {
        System.out.println("给咪咪倒水");
        mimi.drink();
    }
    @Override
    public void wc() {
        mimi.wc();
        System.out.println("给咪咪铲屎");
    }
}
/**测试类*/
class Test{
    public static void main(String[] args) {
        ProxyCat proxyCat=new ProxyCat();
        proxyCat.eat();
        proxyCat.drink();
        proxyCat.wc();
    }
}
---------------------------------------------------------------------------------------------
实例化铲屎官
给咪咪倒猫粮
咪咪吃饭
给咪咪倒水
咪咪喝水
咪咪上厕所
给咪咪铲屎

代理模式UML图:代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
代理模式UML图
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。

二.Java动态代理

Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念,Java动态代理的实现方式主要有以下几种。

  • 基于 JDK 实现动态代理。
  • 基于CGlib 动态代理模式 。
  • 基于 Aspectj 实现动态代理。

基于JDK实现动态代理

通过JDK提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并重写InvocationHandler方法 )字节码文件并实例化对象回调。(jdk动态代理是由java内部的反射机制来实例化代理对象,并代理的调用委托类方法)

/**
 * 猫咪接口
 */
public interface Cat {
    void eat();
    void drink();
    void wc();
}

/**
 * 猫咪实体类:咪咪
 * 实现猫咪接口
 */
class MiMi implements Cat {
    @Override
    public void eat() {
        System.out.println("咪咪吃饭");
    }
    @Override
    public void drink() {
        System.out.println("咪咪喝水");
    }
    @Override
    public void wc() {
        System.out.println("咪咪上厕所");
    }
}
/**
 * 实现InvocationHandler接口的代理类
 */
class CatProxy implements InvocationHandler {

    //需要代理的对象
    Object mimi;


    public CatProxy(Object mimi) {
        super();
        //通过有参构造器将需要代理的对象传入
        this.mimi = mimi;
    }

    //重写invke方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //具体代理处理逻辑
        if ("eat".equals(method.getName())) {
            System.out.println("给咪咪倒猫粮");
            method.invoke(mimi, args);
        } else if ("drink".equals(method.getName())) {
            System.out.println("给咪咪倒水");
            method.invoke(mimi, args);
        } else if ("wc".equals(method.getName())) {
            method.invoke(mimi, args);
            System.out.println("给咪咪铲屎");
        }
        return null;
    }
}
/**测试类*/
class Test {
    public static void main(String[] args) {
        Cat cat = new MiMi();
        CatProxy catProxy = new CatProxy(cat);
        Class<? extends Cat> aClass = cat.getClass();
        //通过Proxy.newProxyInstance方法动态生成字节码并装载实例化出对象
        Cat mi = (Cat) Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), catProxy);
        mi.eat();
        mi.drink();
        mi.wc();

    }
}
------------------------------------------------------------------------------------
给咪咪倒猫粮
咪咪吃饭
给咪咪倒水
咪咪喝水
咪咪上厕所
给咪咪铲屎

JDK动态代理解析

在JDK的动态代理机制中,分别由一个接口和类组成,一个是 InvocationHandler接口、另一个则是 Proxy类,这一组类和接口是实现我们动态代理所必须用到的。

  1. InvocationHandler:每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
public Object invoke(Object proxy, Method method, Object[] args)
- 这个方法里包含三个参数分别是
proxy:  - 指代代理所生成的那个真实对象
method: - 指代的是我们所要调用真实对象的某个方法的Method对象
args:  - 指代的是调用真实对象某个方法时接受的参数
  1. Proxy:该类即为动态代理类。
newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandlerh)

loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我提供给代理对象的一组接口,由代理对象实现这组接口,这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个处理对象上
JDK动态代理原理
  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

Proxy类,根据我们所提供的接口生成一个实现类,它就是动态代理类。
生成过程是:通过接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它需要调用另一个对象的方法。当然这个被调用的对象不能是接口对象,如果是接口对象,我们就没法增强了,等于饶了一圈又回来了。

所以它调用的是接口对象的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,这个接口里面有个方法,它是所有被代理类的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。

所以可以这么认为实现类代理了InvocationHandler,InvocationHandler代理了我们的包装类,两级代理。

动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。
原理图

基于JDK实现动态代理的特点:

  • 动态代理类:在程序运行时,通过反射机制动态生成。
  • 动态代理类通常代理接口下的所有类。
  • 动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。
  • 动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。
  • Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值