动态代理
概述
代理,在我们日常生活之中就有体现,代购,中介,换IP,商家等等,比如有一家美国的大学,可以对全世界招生留学中介(代理 )
留学中介(代理):帮助这家美国的学校招生,中介是学校的代理中介是代替学校完成招生功能。
代理特点:
- 中介和代理他们要做的事情是一致的:招生。
- 中介是学校代理,学校是目标。
- 家长——–>中介(学校介绍,办理入学手续)——–>美国学校。
- 中介是代理,收取费用。
为什么要找中介?
- 中介是专业的,方便。
- 家长现在不能自己去找学校,家长没有能力访问学校或美国学校不接收个人来访。
- 买东西都是商家卖,商家是某个商品的代理,你个人买东西,肯定不会让你接触到厂家的。
静态代理
代理模式作用
- 功能增强:在你原有的功能上,增加了额外的功能:新增加的功能,叫做功能增强。
- 控制访问:代理类不让你访问目标,例如商家不让用户访问厂家。
实现代理方式
- 静态代理:手动增加代理类,在原有代理类基础上增加功能。
- 动态代理:使用JDK反射实现动态添加代理的方法。
静态代理
例子:
模拟一个用户购买U盘的行为。
- 用户:消费者、只能从商家购买商品。
- 商家:代理类、代理U盘品牌的商品。
- 厂家:目标类。
- 三者之间的关系:用户(客户端)—>商家(代理)—>厂家(目标)。
- 总结:商家和厂家都是卖某品牌的U盘,他们完成的功能是一致的。
实现步骤:
- 创建一个接口,定义卖U盘的方法,表示你的厂家和商家做的事情。
- 创建厂家类,实现一步骤的接口。
- 创建商家,就是代理,也需要实现一步骤中的接口。
- 创建客户端类,调用商家的方法买一个U盘。
静态代理创建
Package结构:
实现类:
// 接口类
public interface UsbSell {
/**
* 购买U盘的方法
* @param amount 一次购买U盘的数量
* @return 购买U盘的总价格
*/
float sell(int amount);
}
// 工厂类
public class UsbKingFactory implements UsbSell {
/**
* 厂家的购买方法
* @param amount 一次购买U盘的数量
* @return 购买U盘的总价格
*/
@Override
public float sell(int amount) {
return amount * 80.0f;
}
}
// 商家类
public class pinxixi implements UsbSell {
@Override
public float sell(int amount) {
UsbKingFactory usbKingFactory = new UsbKingFactory();
// 向厂家发送订单,告诉厂家,我订购了U盘,让厂家发货,并返回报价
float floorPrice = usbKingFactory.sell(amount);
// 在厂家底价的基础上,没件加25元利润
float price = amount * 25.0f + floorPrice;
// 在调用sell()方法后,增强的功能
System.out.println("拼兮兮给您返回优惠券或红包");
return price;
}
}
// 消费者类
public class shopMain {
public static void main(String[] args) {
pinxixi pinxixi = new pinxixi();
float price = pinxixi.sell(2);
System.out.println("拼兮兮U盘的价格是:" + price);
}
}
总结:
- 优点:
- 实现简单。
- 缺点:当你的项目,目标类(厂家类)有很多时候,代理类(商家类)也会随之增多。
- 目标类增多、代理类也会随之增多。
- 接口中方法修规、增加后,众多的目标类和代理类都需要修改。
动态代理
概述
使用JDK的反射机制,创建对象的能力,创建的是代理类的的对象,而不用手动创建类文件更不用手动写具体的Java文件,且在程序执行时,调用JDK提供的方法自动创建代理类的对象。换句话说:动态代理是一种创建Java象的能力,让你不用创建代理类(商家类)就能创建代理类对象,除去了中间商。
静态代理和动态代理对比
- 动态代理可以实现低耦合、高内聚,可以在修改、增加接口方法时,不会影响代理类对象。
- 代理类数量可以很少,不用创建很多的代理方法。
动态代理介绍
- 动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的,不需要定义代理类的java源文件。
- 动态代理其实就是JDK运行期间动态创建Class字节码并加载到JVM。
实现方式
JDK动态代理(理解)
- 使用Java反射包(java.lang.reflect)中的反射包中类和接口实现动态代理的功能,会使用到以下三个类/接口:InvocationHandler、Method、Proxy。
CGLlB动态代理(了解)
- CGLlB原理是继承,CGLlB通过继承目标类,创建它的子类,在子类中重写父类中同名的方法,实现功能的修改。因为CGLlB使用的是继承、重写方法,所以要求目标类何方法均不能使用final。CGLlB的要求目标类比较宽松,只要能继承就可以了。CGLlB在很多的框架中使用,比如Mybatis,Spring框架中都有使用。
- InvocationHandler(调用处理器接口):
- 方法:invoke()抽象方法、需要实现。
- 说明:代理对象要执行的功能代码,即代理类要完成的功能就写在invoke()方法中。
- Method(类):表示目标类中所要调用的方法,通过Method可以执行某个目标类中的方法、 JDK会自动提供目标类方法,无需手动指定。
- 方法:invoke(目标类对象,方法的参数)
- 说明:目标类对象:执行某个目标类中的方法、方法的参数:调用目标类方法的参数。
- 例如:Object obj = method.invoke(service,”参数”);
- 注意:Method.invok()与InvocationHandler接口中invoke()方法是两个方法,只不过是重名,不是一个方法。
- Proxy(类):核心对象,创建代理对象。之前创建对象都是new类的构造方法(),现在可以使用Proxy类的方法,代理new使用。
- 方法(静态方法):public static Object newProxyInstance(Classloader loader , Class<?>[] interfaces , InvocationHandler h);
- 说明:创建代理对象,等同于静态代理中的new UsbKingFactory();
- 参数说明:
- Classloader loader:a.getCalss().getClassLoader()、目标对象类加载器,负责向内存中加载对象(使用反射获取对象)
- Class<?>[] interfaces:目标对象实现的接口,也是通过反射要获取的。
- InvocationHandler h:代理类要完成的功能。
- 返回值:目标类的代理对象。
JDK动态代理创建
目标类实现功能:
- 实现业务逻辑的功能。
代理类实现功能:
- 调用目标类方法并执行目标方法所有功能。
- 基于目标类方法功能调用时,增加功能。
创建步骤:
- 创建接口,定义目标类要完成的功能。
- 床架目标类实现接口。
- 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能。
- 调用目标方法。
- 增强功能。
- 使用Proxy;类的静态方法,创建代理对象,并将返回值转为接口类型。
Package结构:
实现类:
// 接口类
public interface UsbSell {
/**
* 购买U盘的方法
* @param amount 一次购买U盘的数量
* @return 购买U盘的总价格
*/
float sell(int amount);
}
// 代理类
public class UsbKingFactory implements UsbSell {
/**
* 厂家的购买方法(目标类)
* @param amount 一次购买U盘的数量
* @return 购买U盘的总价格
*/
@Override
public float sell(int amount) {
System.out.println("目标类中,执行sell目标方法");
float price = amount * 80.0f;
return price;
}
}
// 动态代理类
// 必须实现InvocationHandler接口,完成代理类要做的功能
// 1.调用目标类中的方法
// 2.功能增强啊
public class MysellHander implements InvocationHandler {
/*
动态代理:目标代理是活动的,不是固定的,需要传进来,穿的是什么就是给谁创建代理。
*/
private Object target = null;
public MysellHander(Object target) {
// 给目标对象赋值
this.target = target; // == float price = amount * 25.0f + floorPrice;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 向厂家发送订单,告诉厂家,我订购了U盘,让厂家发货,并返回报价
Object res = null;
// 执行目标类方法
res = method.invoke(target, args); // == float floorPrice = usbKingFactory.sell(amount);
// 在厂家底价的基础上,没件加25元利润
if (res!=null) {
Float price = (Float) res;
price = 25.0f * (Integer)args[0] + price;
res = price;
}
// 在调用sell()方法后,增强的功能
System.out.println("拼兮兮给您返回优惠券或红包");
// 增加后的价格
return res;
}
}
// 测试类
public class shopMain {
public static void main(String[] args) {
// 1.创建目标对象
UsbSell factory = new UsbKingFactory();
// 2.创建InvocationHandler对象
MysellHander handler = new MysellHander(factory);
// 3.创建代理对象
UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), handler);
// 4.通过代理执行方法
float price = proxy.sell(1);
System.out.println(price);
}
}
注意:
动态代理,必须要有接口出现,如果没有,可以使用CGLlB实现。