动态代理(理解)
这笔记能让你把动态代理掌握到什么程度呢?
1.什么是动态代理?
2.动态代理能做什么?
3.mybatis spring 框架中提到动态代理 你不会懵逼;
第一章:代理的介绍
1.什么是代理
代理 无处不在;
中介,商家,代购等等;
比如有一个美国大学,面向全世界招生, 留学中介(代理);
留学中介(代理):帮助这家美国学校招生,中介就是学校的代理,中介代替学校招生;
这个 中介就是代理
代理的特点
1.中介和代理他们要做的事情是一致的:招生;
2.中介是学校的代理,学校是目标
3.家长------中介(代理)------学校(目标)
4.中介是代理,不能白干活,需要收取费用
5.代理不让你访问到 目标;
为什么要找中介呢?
1.中介专业 方便;
2.家长不方便直接去找学校,家长不能直接访问学校,或者学校不接受个人来访 只接受代理访问
2.开发中的情况
有一个a类, 需要调用c类的方法 ,但是c不让a调用;
a-----不能调用c类的方法
在 a和 c中间创建一个 代理 b ,c让b访问;
a—访问 b(代理)-----访问c(目标);
实际案例:登陆 注册 验证码,验证码是手机短信
中国移动 中国联通能发短信
中国移动 中国联通 有子公司或者关联公司,他们面向社会提供短信发送功能
张三项目发短信------------------->(代理)子公司或者关联公司------------------->(目标)中国移动 中国联通
3.代理好处
使用代理模式的作用/好处:
- 功能增强:在目标功能的基础上,增加额外的功能,新增加的功能叫做功能增强
- 控制访问:代理类不让你访问目标,
- 解耦:将客户端 与 目标对象 分离,在一定程度上降低了系统的耦合度
- 保护: 不让客户端直接访问 目标对象,代理对目标对象起到一定的保护作用
缺点:也是增加了程序的复杂度
第二章:静态代理
静态代理
- 代理类是自己手工实现的,自己创建一个类,表示为代理类
- 同时你的目标对象也是明确的
- 优点:
- 实现简单 ,容易理解
- 缺点:
- 当你的项目中,有很多的目标类和代理类时候 有一下缺点
- 当目标类增加了,代理类有可能成倍的增加,代理类数量过多
- 接口功能的增加或者修改,会影响众多的实现类,目标类,代理类都需要修改
模拟一个用户购买U盘的案例
关系分析:
用户:是客户端类;
商家:是代理类,代理某个品牌的U盘
厂家:是目标;
三者关系:用户购买U盘----- (代理)商家------>(目标)厂家
商家和厂家 都是卖U盘的 ,他们完成的功能是一致的,都是卖U盘的;
实现步骤:
1.创建一个接口,定义卖U盘的方法,表示你的厂家和商家做的事情;
(依赖倒置原则:尽量面向接口编程[高层模块不应该以来底层模块,都应该依赖于抽象; 抽象不应该依赖于细节,细节应该依赖于抽象])
2.创建厂家类,实现步骤1
3.创建商家类 实现步骤1,并且聚合厂家类(合成复用原则:尽量先使用组合或者聚合等关联关系;)
4.创建客户端类,调用商家的卖U盘的方法;
package chapter03;
//目标类
public class UsbFactory implements UsbSell{
@Override
public float sellUsb(int num) {
//厂家 1000个卖95 1W个 85 10W个75
//厂家的出厂价
return 75l;
}
}
package chapter03;
//代理类
public class TaoBao implements UsbSell{
//组合关系
private UsbFactory usbFactory =new UsbFactory();
@Override
public float sellUsb(int num) {
float v = usbFactory.sellUsb(num);
//增强功能: 增加了价格;
//在你的目标类方法调用之后 ,你做的其他功能 都是增强功能;
float c = (v+25)*num;
//增加后的价格
return c;
}
}
package chapter03;
public class User {
public static void main(String[] args) {
TaoBao taoBao = new TaoBao();
float v = taoBao.sellUsb(2);
System.out.println("向买了两个U盘花费"+v);
}
}
第三章 动态代理
1.动态代理的介绍
动态代理
我们可以使用动态代理去避免静态代理的缺点;
1.代理类数量可以很少;
2.当修改了接口的方法时,不会影响代理类
动态代理:在程序的执行的过程中,使用jdk的反射机制,创建代理类对象 ,并且动态的指定要代理的目标类
再详细的解释:
- 动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的。动态代理不需要定义代理类的,java源文件。
- 动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM。
- 动态代理的实现方式常用的有两种:使用JDK代理,与通过CGLlB动态代理。
动态代理的实现:
- jdk动态代理(理解):使用java反射包中的类和接口实现动态代理的功能,反射包java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy
- cglib动态代理(了解): cglib是第三方的工具库,创建代理对象
- cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中
重写父类中同名的方法,实现功能的修改。 - 因为cglib是继承,重写方法,所以要求目标类不能是fina1的,方法也不能是final的。cglib的要求目标类比较宽松,只要能继承就可以了。cglib在很多的框架中使用,
比如mybatis,spring框架中都有使用
- cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中
2.回顾反射
Method类的结构图
- Class Method
- java.lang.Object
- java.lang.reflect.AccessibleObject
- java.lang.reflect.Executable
- java.lang.reflect.Method
- java.lang.reflect.Executable
- java.lang.reflect.AccessibleObject
- java.lang.Object
Method对象调用invoke(Object obj, Object… args);
第一个参数 :表示对象,表示执行这个对象的方法;
第二个参数:表示执行时,参数值;
返回值Object ,方法执行后的返回值;
3.JDK动态代理:
使用到的 三个类 :
InvocationHandler接口(调用处理器):就一个 方法 invoke();
invoke();表示代理对象要执行的功能代码。代理类要完成的功能就卸载 invoke();中
- 调用目标对象的方法并执行 2.功能增强,在目标方法调用时增加功能
invoke()方法原型:
// Object proxy :创建的代理对象 ,无需赋值
//Method method 目标类中的方法 无需赋值
//Object[]args 目标类中方法的参数 无需赋值
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
**Method 类:表示方法的,确切的说就是目标类中的方法 **
作用:通过Method可以执行某个目标类的方法,Method.invoke(目标对象,方法参数);
Proxy类:核心对象,创建代理对象。
静态方法: newProxyInstance(); 该方法是用来创建代理对象;
newProxyInstance()方法原型
//ClassLoader loader 类加载器负责向内存中加载对象;
// Class<?>[] interfaces 目标对象实现的接口类
// InvocationHandler h 我们自己写的代理类要完成的功能
//返回值 Object 就是代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
代码实现:
1.创建接口,定义目标类要完成的功能
2.创建目标类实现接口
3.创建 InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
4.使用Proxy的静态方法,创建代理对象。并把返回值类型转为接口类型
package cha04_dynamic_proxy.service;
//接口
public interface UsbSell {
float usbSell(int num);
}
-------------------------------------------------------
package cha04_dynamic_proxy.factory;
import cha04_dynamic_proxy.service.UsbSell;
//目标对象
public class UsbFactory implements UsbSell {
@Override
public float usbSell(int num) {
return 75*num;
}
}
-----------------------------------------------------------
package cha04_dynamic_proxy.handler;
import cha04_dynamic_proxy.service.UsbSell;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MySellHandller implements InvocationHandler {
private UsbSell target=null;
//这里使用的聚合关系, 传进来哪个目标对象 就给谁创建代理
public MySellHandller(UsbSell target) {
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理类要做的事情 :
//1. 调用目标对象方法并执行;
Object invoke = method.invoke(target, args);
//2.增强功能
float price = 0.00f;
if (invoke !=null){
price = (float) invoke;
price=price+(25*(int)args[0]);
}
return price;
}
}
--------------------------------------------------------------------------------
package cha04_dynamic_proxy;
import cha04_dynamic_proxy.factory.UsbFactory;
import cha04_dynamic_proxy.handler.MySellHandller;
import cha04_dynamic_proxy.service.UsbSell;
import java.lang.reflect.Proxy;
public class MainShop {
public static void main(String[] args) {
//创建目标对象
UsbSell usbSell = new UsbFactory();
//创建invocationHandller 对象
MySellHandller handller =new MySellHandller(usbSell);
//创建代理对象
UsbSell proxy = (UsbSell) Proxy.newProxyInstance(usbSell.getClass().getClassLoader(),
usbSell.getClass().getInterfaces(),
handller);
//通过代理对象 执行方法;
float v = proxy.usbSell(2);
System.out.println("动过动态代理对象调用方法"+v);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QBUxwYsh-1644026320155)(D:\笔记\反射\JDK动态代理.assets\image-20220128140845392.png)]
4.CGLIB动态代理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rgMviLAQ-1644026320156)(D:\笔记\反射\JDK动态代理.assets\image-20220128114615663.png)]
需要导入jar包:核心包和依赖包(spring_core.jar已经集成了这两个包,因此,导入此包即可)
子类是在调用的时候才生成的
使用目标对象的子类的方式实现的代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展,能够在运行时动态生成字节码,可以解决目标对象没有实现接口的问题
缺点:被final或static修饰的类不能用cglib代理,因为它们不会被拦截,不会执行目标对象的额外业务方法
CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code 生成类库,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP。
使用JDK的Proxy实现代理,要求目标类与代理类实现相同的接口。若目标类不存在接口,则无法使用该方式实现。
但对于无接口的类,要为其创建动态代理,就要使用CGLIB来实现**。CGLIB代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。**所以,使用CGLIB生成动态代理要求自标类必须能够被继承,即不能是final的类。
cglib经常被应用在框架中,例如Spring ,Hibernate等。Cglib 的代理效率高于Jdk。对于cglib一般的开发中并不使用。做了一个了解就可以。
ation Library)是一个开源项目。是一个强大的,高性能,高质量的Code 生成类库,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP。
使用JDK的Proxy实现代理,要求目标类与代理类实现相同的接口。若目标类不存在接口,则无法使用该方式实现。
但对于无接口的类,要为其创建动态代理,就要使用CGLIB来实现**。CGLIB代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。**所以,使用CGLIB生成动态代理要求自标类必须能够被继承,即不能是final的类。
cglib经常被应用在框架中,例如Spring ,Hibernate等。Cglib 的代理效率高于Jdk。对于cglib一般的开发中并不使用。做了一个了解就可以。