Java基础之——动态代理

动态代理

动态代理: 基于反射机制。

1、动态代理总括

1.1、★ 什么是动态代理 ?

使用 jdk 的反射机制,创建对象的能力, 创建的是代理类的对象。 而不用你创建代理类文件。不用写java文件。

动态: 在程序执行时,调用 jdk 提供的方法才能创建代理类的对象。

注意: jdk 动态代理,必须有接口目标类必须实现接口, 没有接口时,需要使用 cglib 动态代理。

1.2、★ 知道动态代理能做什么 ?

可以在不改变原来目标方法功能的前提下, 可以在代理中增强自己的功能代码。

eg.
你所在的项目中,有一个功能是其他人(公司的其它部门,其它小组的人)写好的,你可以使用,

GoNong.class ,  GoNong gn = new GoNong(), gn.print();

你发现这个功能,现在还缺点, 不能完全满足我项目的需要。 我需要在 gn.print() 执行后,需要自己在增加代码。

代理实现 gn.print() 调用时, 增加自己代码, 而不用去改原来的 GoNong 文件。


2、什么是代理?

代购, 中介,换 ip,商家等等。

比如有一家美国的大学, 可以对全世界招生。 留学中介(代理)

留学中介(代理): 帮助这家美国的学校招生, 中介是学校的代理, 中介是代替学校完成招生功能。

代理特点:

  1. 中介和代理他们要做的事情是一致的: 招生;
  2. 中介是学校代理, 学校是目标;
  3. 家长 — 中介(学校介绍,办入学手续)---- 美国学校;
  4. 中介是代理,不能白干活,需要收取费用;
  5. 代理不让你访问到目标。

在开发中也会有这样的情况, 你有 a 类, 本来是调用 c 类的方法, 完成某个功能。 但是 c 不让 a 调用。

a ----- 不能调用 c的方法。
在 a 和 c 直接 创建一个 b 代理,c 让 b 访问。 
a -- 访问b --- 访问c

实际的例子: 登录,注册有验证码, 验证码是手机短信。

中国移动, 联通能发短信。 

中国移动, 联通能有子公司,或者关联公司,他们面向社会提供短信的发送功能

张三项目发送短信 ---- 子公司,或者关联公司 ----- 中国移动, 联通

3、★ 使用代理模式的作用

  1. 功能增强: 在你原有的功能上,增加了额外的功能。 新增加的功能,叫做功能增强。
  2. 控制访问: 代理类不让你访问目标,例如商家不让用户访问厂家。

4、代理的分类

  • 静态代理:①代理类是自己手工实现的,自己创建一个 java 类,表示代理类。②同时你所要代理的目标类是确定的。

    优点:

    1. 实现简单
    2. 容易理解

    缺点【当项目中,目标类和代理类很多时候】:

    1. 当目标类增加了, 代理类可能也需要成倍的增加。 代理类数量过多;
    2. 当你的接口中功能增加了, 或者修改了,会影响众多的实现类,厂家类,代理都需要修改。
      影响比较多。

    在静态代理中目标类很多时候,可以使用动态代理,避免静态代理的缺点。

  • 动态代理:在程序执行过程中,使用 jdk 的反射机制,创建代理类对象, 并动态的指定要代理目标类。

    优点:

    1. 代理类数量可以很少
    2. 当你修改了接口中的方法时,不会影响代理类

    缺点:

    1. 实现复杂
    2. 不容易理解

5、★代理类的功能

  1. 调用目标方法,执行目标方法的功能
  2. 功能增强:在目标方法调用时,增加功能

6、静态代理的实现

6.1、业务需求

模拟一个用户购买 u 盘的行为:

用户是客户端类;

商家:代理,代理某个品牌的 u 盘。

三者的关系: 用户(客户端)— 商家(代理【类】)— 厂家(目标【类】)

商家和厂家都是卖 u 盘的,他们完成的功能是一致的,都是卖 u 盘。

6.2、实现步骤

  1. 创建一个接口,定义卖 u 盘的方法, 表示你的厂家和商家做的事情;
  2. 创建厂家类,实现步骤 1 的接口;
  3. 创建商家,就是代理,也需要实现步骤 1 中的接口;
  4. 创建客户端类,调用商家的方法买一个 u 盘。

6.3、代码模板

6.3.1、接口【表示功能的,厂家,商家都要完成的功能】
public interface UsbSell {
    //返回值表示一个u盘的价格。
    float sell(int count);
}
6.3.2、目标类【厂家】
package org.example.factory;

import org.example.service.UsbSell;

//目标类: 金士顿厂家, 不接受用户的单独购买。
public class UsbKingFactory implements UsbSell {
    @Override
    public float sell(int count) {
        System.out.println("目标类中的方法调用 , UsbKingFactory 中的sell ");
        //一个128G的u盘是 85元。
        return 85;
    }
}
6.3.3、代理类【某公司】
package org.example.enterprise;

import org.example.factory.UsbKingFactory;
import org.example.service.UsbSell;

public class JD implements UsbSell {
    //声明 商家代理的厂家具体是谁
    private UsbSell factory = new UsbKingFactory();

    @Override
    public float sell(int count) {
        //向厂家发送订单,告诉厂家,我买了u盘,厂家发货
        float price = factory.sell(1);
        //商家 需要加价, 也就是代理要增加价格。
        price += 40;//增强功能,代理类在完成目标类方法调用后,增强了功能。
        System.out.println("京东送您100京东豆!");

        return price;
    }
}
6.3.4、客户端【我们自己】
package org.example;

import org.example.enterprise.JD;
import org.example.enterprise.TaoBao;

public class Customer {
    public static void main(String[] args) {
/*        TaoBao t = new TaoBao();
        float price = t.sell(1);
        System.out.println(price);*/

        JD j = new JD();
        float price = j.sell(1);
        System.out.println(price); //41
    }
}

6.4、总结

  1. 此时我们只有一家 JD 卖金士顿 u 盘,当在来一家 TaoBao 也要卖金士顿 u 盘时,就要再写一个代理类,这要是有 100 家企业要买 u 盘就要写 100 个代理类,会显得代理类数量过多。并且当目标类【u 盘厂家】多起来了之后,代理类都是成倍数上涨的!
  2. 当你的接口中功能增加了, 或者修改了,会影响众多的实现类,厂家类,代理都需要修改。影响比较多。

7、★ 动态代理

7.1、总括

  1. 动态代理是一种创建 java 对象的能力,让你不用创建 JD 类,就能创建代理类对象。
  2. 动态代理使用 java 反射包中的类和接口实现动态代理的功能
  3. 动态代理使用到的三个类:反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy。

7.2、动态代理的分类

  1. jdk 动态代理:
    1. 使用 java 反射包中的类和接口实现动态代理的功能;
    2. 使用反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy。
  2. cglib 动态代理:
    1. cglib 是第三方的工具库, 创建代理对象;
    2. cglib 的原理是继承, cglib 通过继承目标类,创建它的子类,在子类中重写父类中同名的方法, 实现功能的修改;
    3. 因为 cglib 是继承,重写方法,所以要求目标类不能是 final 的, 方法也不能是 final 的;
    4. cglib 的要求目标类比较宽松, 只要能继承就可以了。cglib 在很多的框架中使用, 比如 mybatis ,spring 框架中都有使用。

7.3、★ InvocationHandler , Method, Proxy 解析

7.3.1、InvocationHandler【接口】
  1. 表示你的代理类要干什么;
  2. InvocationHandler 接口(调用处理器):就一个方法 invoke();
  3. invoke():表示代理对象要执行的功能代码。你的代理类要完成的功能就写在 invoke() 方法中。
7.3.1.1、 invoke()方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

参数:

  • Object proxy:jdk 创建的代理对象,无需赋值;
  • Method method:目标类中的方法,jdk 提供 method 对象的;
  • Object[] args:目标类中方法的参数, jdk 提供的
7.3.1.2、★ 怎么用?
  1. 创建类实现接口 InvocationHandler
  2. 重写 invoke() 方法, 把原来静态代理中代理类要完成的功能,写在这。
7.3.2、Method
  1. 通过 Method 可以执行某个目标类的方法,Method.invoke();

    method.invoke(调用该方法的对象,方法的参数);
    

注意: Method 类中的 invoke() 方法和 InvocationHandler 接口的 invoke() 方法只是名字一样,两个并不一样!

7.3.3、Proxy

核心的对象,创建代理对象。之前创建对象都是 new 类的构造方法() ,现在我们是使用 Proxy 类的方法,代替 new 的使用。

7.3.3.1、newProxyInstance() 方法【静态方法】
  • 作用:创建代理对象, 等同于静态代理中的 TaoBao taoBao = new TaoBao();

  • 方法原型:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    

    参数:

    • ClassLoader loader:类加载器,负责向内存中加载对象的。 使用反射获取对象的 ClassLoader.【类a , a.getCalss().getClassLoader(), 目标对象的类加载器】
    • Class<?>[] interfaces: 接口, 目标对象实现的接口,也是反射获取的。【类a , a.getClass().getInterfaces().】
    • InvocationHandler h : 我们自己写的,代理类要完成的功能
    • 返回值:就是代理对象

8、动态代理的实现

8.1、业务需求

模拟一个用户购买 u 盘的行为

用户是客户端类

商家:代理,代理某个品牌的 u 盘

三者的关系: 用户(客户端)— 商家(代理【类】)— 厂家(目标【类】)

商家和厂家都是卖u盘的,他们完成的功能是一致的,都是卖 u 盘。

8.2、实现步骤

  1. 创建一个接口,定义卖 u 盘的方法, 表示你的厂家和商家做的事情;
  2. 创建厂家类,实现步骤 1 的接口;
  3. 创建类实现接口 InvocationHandler 重写 invoke() 方法, 把原来静态代理中代理类要完成的功能,写在这;
  4. 创建客户端类,调用商家的方法买一个 u 盘。

8.3、代码模板

8.3.1、接口【表示功能的,厂家,商家都要完成的功能】
package org.example.service;

public interface UsbSell {
    float sell(int count);
}
8.3.2、目标类【厂家】
package org.example.factory;

import org.example.service.UsbSell;

public class UsbKingFactory implements UsbSell {
    @Override
    public float sell(int count) {
        System.out.println("目标类中的方法调用 , UsbKingFactory 中的sell ");
        return 85;
    }
}
8.3.3、创建类实现接口 InvocationHandler
package org.example.handler;

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

//必须实现InvocationHandler接口,完成代理类要做的功能(1.调用目标方法,2.功能增强)
public class MySellHandler implements InvocationHandler {
    //传入进来的对象
    private Object target;

    //动态代理:目标对象是活动的,不是固定的,需要传入进来。
    //传入是谁,就给谁创建代理。
    public MySellHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        //float price = factory.sell(1);
        res = method.invoke(target, args);//执行目标方法

        //商家 需要加价, 也就是代理要增加价格。
        //price = price + 25; //增强功能,代理类在完成目标类方法调用后,增强了功能。
        if (res != null){
            Float price = (Float) res;
            price += 25;
            res = price;
        }
        //在目标类的方法调用后,你做的其它功能,都是增强的意思。
        System.out.println("淘宝送您优惠券一张!");


        return res;
    }
}
8.3.4、客户端【我们自己】
package org.example;

import org.example.factory.UsbKingFactory;
import org.example.handler.MySellHandler;
import org.example.service.UsbSell;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Customer {
    public static void main(String[] args) {
        //创建代理对象,使用Proxy

        //1. 创建目标对象
        UsbSell factory = new UsbKingFactory();

        //2.创建InvocationHandler对象
        InvocationHandler handler = new MySellHandler(factory);

        //3.创建代理对象
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), handler);

        //com.sun.proxy.$Proxy0 : 这是jdk动态代理创建的对象类型。
        System.out.println(proxy.getClass().getName());

        //4.通过代理执行方法
        float price = proxy.sell(1);//执行的是MySellHandler类里面的invoke方法
        System.out.println(price);
    }
}

8.4、程序执行流程

在这里插入图片描述

程序执行到 proxy.sell(1) 语句会调用 InvocationHandler 实现类的 invoke() 方法,然后一次执行下来,最终返回值返回给方法调用处 proxy.sell(1)

注意:proxy 是com.sun.proxy.$Proxy0,所以才会调用 InvocationHandler 实现类的 invoke() 方法。


9、★ 实现动态代理步骤

  1. 创建接口,定义目标类要完成的功能;
  2. 创建目标类实现接口;
  3. 创建 InvocationHandler 接口的实现类,在 invoke 方法中完成代理类的功能
    1. 调用目标方法
    2. 增强功能
  4. 使用 Proxy 类的静态方法,创建代理对象。 并把返回值转为接口类型。

10、动态代理代码模板

10.1、InvocationHandler 接口实现类模板

public class MySellHandler implements InvocationHandler {
    //传入进来的对象
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = null;
        //float price = factory.sell(1);
        res = method.invoke(target, args);//执行目标方法
		
		//增强功能代码

        return res;
    }
}

10.2、客户端类模板

public class Customer {
    public static void main(String[] args) {
        //创建代理对象,使用Proxy

        //1. 创建目标对象
        UsbSell factory = new UsbKingFactory();

        //2.创建InvocationHandler对象
        InvocationHandler handler = new MySellHandler(factory);

        //3.创建代理对象
        UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), handler);

        //4.通过代理执行方法
        float price = proxy.sell(1);//执行的是MySellHandler类里面的invoke方法
        System.out.println(price);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值