日常生活中代理
掌握的程度
- 基于反射机制
- 什么是动态代理
- 使用jdk的反射机制,创建对象的能力,创建的代理类的对象,而不用你创建类文件,不用写java文件
- 动态: 在程序执行时候,调用jdk提供的方法才能创建代理类的对象
- 知道动态代理能做什么?
什么是动态代理
比如有一家美国的大学,可以对全世界进行招生。(代理,留学中介)
留学中介(代理): 帮助这家美国学校进行招生。中介是学校的代理,中介是代替学校完成招生的功能
代理特点:
- 中介和代理他们要做的事情是一致的:招生
- 中介是学校的代理,学校是目标
- 家长 —-> 中介(学校介绍,办理入学手续) ——> 美国学校
- 中介是代理,不能白干活,需要收取费用
- 代理不让你访问到目标。
为什么要找中介?
- 中介是专业的,方便
- 家长现在不能自己去找学校,家长没有能力访问学校。或者美国学校不能接受个人来访
买东西都是商家卖,商家是某个商品的代理,你个人买东西,肯定不会让你接触到厂家的。
在开发中也会有这样的情况
- 你有a类,本来是调用c类的方法,完成某个功能,但是c不让a调用
- a —- 不能调用c类的方法
- 在 a 和 c 直接创建一个 b 代理,c让b访问
- a —> 访问b —-> 访问c
实际例子
- 登录注册有验证码,验证码是手机短信
- 你没有短信发送的能力,中国移动和中国联通可以发短信
- 移动和联通有子公司或者关联公司,他们面向社会提供短信的发送功能
张三项目要发送短信 —-> 子公司或者关联公司 —-> 中国移动,联通
代理模式的作用
功能增强: 在你的原有的功能上,增加了额外的功能,新增加的功能,叫做功能增强
-
静态代理
特性:
代理类是自己手工实现的,自己要创建一个java类,表示代理类
- 同时,你所要的代理的目标类的确定的
- 实现简单,理解容易
模拟一个用户购买u盘的行为
- 用户是客户端类
- 商家: 代理,代理某个拼盘的u盘
- 厂家: 目标类
- 三者的关系 : 用户—-> 商家 —-> 厂家
- 商家和厂家都是卖u盘的,他们完成的功能一致
实现的步骤
- 创建一个接口定义卖u盘的方法,表示厂家和商家做的事情
- 创建厂家类,实现 1步骤的接口
- 床架商家,就是代理,也是现实1步骤中的接口
- 创建客户端类,调用商家 的方法卖一个u盘
静态代理接口和目标类的创建
创建接口
package com.itvip666.proxy.service;
/**
* Created by gysui on 2020/10/11
* 表示功能,厂家和商家都要完成的功能
*/
public interface UsbSell {
/**
* 卖u盘的方法
* @param amount 一次购买的数量
* @return 表示一个u盘的价格
*/
float sell(int amount);
}
创建金士顿厂家
/**
* Created by gysui on 2020/10/11
* 目标类,金士顿厂家,不接受用户的单独购买
*/
public class UsbKingFactory implements UsbSell {
/**
* 卖u盘的方法
*
* @param amount 一次购买的数量
* @return 表示一个u盘的价格
*/
@Override
public float sell(int amount) {
// 1个128G U盘是85元
// 后期可以根据 aumount 实现不同的价格,例如10000个,单价是80,50000个是75
return 85.0f
;
}
}
创建淘宝代理
package com.itvip666.proxy.service.shangjia;
import com.itvip666.proxy.service.UsbSell;
import com.itvip666.proxy.service.factory.UsbKingFactory;
/**
* Created by gysui on 2020/10/11
* 淘宝是一个商家,代理金士顿u盘的销售
*/
public class TaoBao implements UsbSell {
// 声明 商家代理的厂家具体是谁
private UsbSell factory = new UsbKingFactory();
/**
* 实现销售u盘的功能
*
* @param amount 一次购买的数量
* @return 表示一个u盘的价格
*/
@Override
public float sell(int amount) {
// 向厂家发送订单,告诉厂家,我买了u盘,厂家发货
float price = factory.sell(amount);
// 商家需要加价,也就是代理要增加价格
// 增强功能,代理类在完成目标类方法调用后,增强的功能
// 在目标类的方法调用后,你做的其他功能,都是增强的意思
price = price + 25;
return price;
}
}
客户端购买u盘
package com.itvip666.proxy;
import com.itvip666.proxy.service.shangjia.TaoBao;
/**
* Created by gysui on 2020/10/11
*/
public class ShopMain {
public static void main(String[] args) {
// 创建代理的淘宝对象
TaoBao taoBao = new TaoBao();
float price = taoBao.sell(234);
System.out.println("通过淘宝的商家,购买u盘的单价 = " + price);
}
}
代理类完成的功能
当你的项目中目标类和代理类有很多的时候,有以下缺点:
动态代理
- 在程序执行的过程中,使用JDK的反射机制,创建代理类对象,并动态的指定要代理目标类 (厂家)
- 换句话说,动态代理是一种创建java对象的能力,让你不用创建taobao类,就能创建代理对象
动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用、Java注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。
本文主要介绍Java中两种常见的动态代理方式:JDK原生动态代理和CGLIB动态代理。
动态代理优点
使用java反射包中的类和接口实现动态代理的功能
反射包 java.lang.reflect,里面有三个类
- InvocationHandler
- Method
- Proxy
使用JDK的 Proxy实现代理
- 要求目标类与代理类实现相同的接口。若目标类不存接口,则无法使用该方式实现
-
CGLIB动态代理(了解)
cglib是第三方的工具库,创建代理对象
- cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改
- 因为cglib是继承,重写方法,所以要求目标类不能是 final的,方法也不是final的
- cglib要求目标类比较宽松,只要能继承就可以了,cglib在很多框架中使用,比如mybatis,spring框架中都有使用
动态代理制反射 Method
Method类,表示方法。类中的方法,通过Method可以执行某个方法
/**
* Created by gysui on 2020/10/11
*/
public class TestMain {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// HelloService helloService = new HelloServiceImpl();
// helloService.say("杨洋");
// 使用反射机制执行say方法, 核心 Method (类中的方法)
HelloService target = new HelloServiceImpl();
Method method = HelloService.class.getMethod("say", String.class);
// 通过method可以执行say的方法的调用
/**
* 表示执行方法的调用
* 参数1: 表示对象,要执行这个对象的xxx方法
* 参数2: 方法的参数
* 返回值: Object表示方法执行后的返回值
*/
method.invoke(target,"小阳");
}
}
说明
- method.invoke() 就是用来执行目标方法的,等同于静态代理中的
// 向厂家发送订单,告诉厂家,我买了u盘,厂家发货
float price = factory.sell(amount);
InvocationHandler (调用处理器)
- 是一个接口,就有一个方法
invoke()
- invoke() 表示代理对象要执行的功能代码
- 你的代理类要完成的功能就写在invoke()方法中
- 代理类完成的功能
- 调用目标方法,执行目标方法
- 功能增强: 在目标方法调用后需要做的事情
参数:
- Object proxy : jdk创建的代理对象,无需赋值
- method : 目标类中的方法 , jdk提供的method对象
- args : 目标类中的方法参数 ,Jdk提供
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
怎么用:(== InvocationHandler 接口表示代理要做什么==)
作用: 创建代理对象
之前创建对象方式
- new 类的构造方法
现在我们使用Proxy类的方法,代替new的使用
方法: 静态方法 newProxyInstance()
作用: 创建代理对象,等同于静态代理对象 new TaoBao()
参数
- ClassLoader loader : 类加载器,负责向内存中加载对象的
- 使用反射获取对象的 ClassLoader
- 比如有一个类a , a.getClass().getClassLoader()
- 这个是目标对象的类加载器
- Class<?>[] interfaces : 目标所实现的接口,也是通过反射获取的
- InvocationHandler h : 我们自己写的,代理类要完成的功能
- 返回值就是代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
jdk动态代理实现
步骤:
- 创建接口,定义目标类要完成的功能
- 创建目标类实现接口
- 创建 InvocationHandler 接口的实现类,在invoke方法中完成代理类的功能
- 调用目标方法
- 增加功能
- 使用 Proxy类的静态方法来创建代理对象,并把返回值转为接口类型
目标接口
/**
* Created by gysui on 2020/10/11
* 目标接口
*/
public interface UsbSell {
float sell(int amount);
}
金士顿工厂接口
/**
* Created by gysui on 2020/10/11
* 目标类
*/
public class UsbKingFactory implements UsbSell {
/**
* 目标方法
* @param amount
* @return
*/
@Override
public float sell(int amount) {
System.out.println("目标类中执行了sell目标方法");
return 85.0f;
}
}
InvocationHandler 接口的实现类
/**
* Created by gysui on 2020/10/11
* 必须实现 InvocationHandler接口,完成代理类要做的功能
* 1. 调用目标方法
* 2. 功能增强
*/
public class MySellHandler implements InvocationHandler {
private Object target = null;
/**
* 动态代理: 目标对象是活动的,不是固定的,需要传进来
* 传入谁,就给谁创建代理
* @param target
*/
public MySellHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
// 向厂家发送订单,告诉厂家,我买了u盘,厂家发货
// 执行目标方法
res = method.invoke(target,args);
// 商家需要加价,也就是代理要增加价格
// 增强功能,代理类在完成目标类方法调用后,增强的功能
// 在目标类的方法调用后,你做的其他功能,都是增强的意思
// price = price + 25;
if (res != null) {
Float price = (Float) res;
price = price + 25;
res = price;
}
return res;
}
}
使用 Proxy类的静态方法来创建代理对象,并把返回值转为接口类型
/**
* Created by gysui on 2020/10/11
*/
public class ProxyMain {
public static void main(String[] args) {
// 1. 创建目标对象
UsbSell usbSell = new UsbKingFactory();
// 2. 创建InvocationHandler对象
InvocationHandler invocationHandler = new MySellHandler(usbSell);
// 3. 创建代理
UsbSell proxy = (UsbSell) Proxy.newProxyInstance(usbSell.getClass().getClassLoader(),
usbSell.getClass().getInterfaces(), invocationHandler);
// com.sun.proxy.$Proxy0 这是jdk动态代理创建的对象类型
System.out.println("proxy = " + proxy.getClass().getName());
// 4. 通过代理执行方法
float sell = proxy.sell(1);
System.out.println("通过动态代理对象,调用方法 = " + sell);
}
}
动态代理项目中的应用
在程序开发中的意思:
- 比如你所在的项目中,有一个功能是其他人写好的 (公司的其他部门或者其他小组)
- GoNong.class , GoNong gn = new GONong() gn.print()
- 但是现在你发现,现在功能缺点,不能完全满足我项目的需要,需要在gn.print执行后,需要自己增加代码
- 用代理实现gn.print()调用时候,增加自己代码,而不是改原来的GoNong文件