学习代理模式内容:
★ 静态代理、
★ 动态代理(JDK动态代理、CGLIB动态代理)、
★ 拦截器的原理和日志记录
★ 代理总结
一、职责分离的例子---房屋租赁
1、重复
2、职责不分离
●【陪着看房、陪着谈价格、交钥匙
】----不应该交个房东来重复做,不是他关心的重点,作为房东他只需要关心【签合同、收房租
】
----解决:把这部分重复非业务重点的代码重构抽离给第三者
----中介
● 抽离之后,中介也会重复了呀,那怎么办呢?
----没事,这是人家的责任,人家就是靠这个赚钱的。
✿ 客户端Client----- 第三方(目的:增强Service的功能)-----服务端Service
二、代理模式 [设计模式] 中介作用
✿ 1、代理模式:客户端直接使用的都是代理对象
,并不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介作用
。
(1)代理对象完全包含真实对象,客户端使用的都是代理对象的方法,和真实对象没有直接关系;
(2)代理模式的职责:把不是真实对象该做的事情从真实对象上撇开——职责清晰;
三、静态代理
1、概念/原理:
在程序运行前就已经存在代理类的字节码文件,代理对象和真实对象的关系在运行前就确定了。
2、静态代理的实现过程:
■ 将功能封装成一个接口,代理类和真实类/委托类 都需要实现该接口
:
真实类/委托类 需要实现该接口,很好理解,才具有特定的功能。
代理类 需要实现该接口,是因为这样子才知道需要为哪些功能做代理、做增强。
3、静态代理的优缺点:
● 优点:职责分离、安全
1,业务类只需要关注业务逻辑本身,保证了业务类的重用性。
2,把真实对象隐藏起来了,保护真实对象。
● 缺点:代码臃肿(一个真实对象需要对应一个代理对象)、不符合开闭原则(不方便扩展和维护)。
----一个代理类只能服务某一个业务接口
。
1,代理对象的某个接口只服务于某一种类型的对象,也就是说每一个真实对象都得创建一个代理对象。
2,如果需要代理的方法很多,则要为每一种方法都进行代理处理。
3,如果接口增加一个方法,除了所有实现类需要实现这个方法外,代理类也需要实现此方法
4、总结静态代理:
1、静态代理需要实现某个接口(才知道要代理、增强什么功能)
2、静态代理要包含真实对象(真实对象是静态代理类的对象属性)----内部bean,不暴露给外界
3、测试,通过接口对象进行测试,直接调用的是静态代理对象(间接调用真实对象)
【动态代理在测试时,是通过动态代理类,先获取到代理对象,直接调用的是动态代理对象(间接调用真实对象)】
- 获取到代理对象:jdk提供的Proxy的方法newProxyInstance
● 详细的代码:
● 静态代理bean对象的配置:
四、动态代理
1、学习动态动态代理之前的准备工作:字节码的动态加载
(1) 先了解一下java的编译运行原理【java加载字节码原理】:
- 编译:将源文件 .java 文件,通过编译器(javac 命令) 编译成 字节码文件 .class 文件。【
编译得到字节码文件
】 - 运行:通过
类加载器
(以二进制流形式)把字节码加载进JVM,通过java解析器(java 命令) 进行运行程序。【jvm解析字节码文件
】
(2) 如何动态创建一份字节码?(实现了在代码中动态创建一个类的能力):
- 通过java的编译和运行原理,可以看到:在运行时期,是jvm通过字节码的二进制信息来加载类的。
所以,当我们在运行时期,通过java编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类。
2、静态代理和动态代理的(原理)区别:
■ 静态代理:(经历了编译
和运行)
在程序运行前就已经存在代理类的字节码文件
(因为通过了编译阶段),代理对象和真实对象的关系在运行前就确定了
(因为通过了编译阶段)。
■ 动态代理:(只经历了运行
,咱通过某种手段得到的字节码【遵循字节码格式和结构】)
动态代理类是在程序运行期间由jvm通过反射等机制动态生成的,所以不存在代理类的字节码文件
(因为没有经历编译阶段),代理对象和真实对象的关系是在程序运行期间才确定的
。
□ 两个原理相同点:
客户端直接使用的都是代理对象
,并不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介作用
。
3、动态代理分类:JDK动态代理、CGLIB动态代理
(1)JDK动态代理:
① 使用JDK的 Proxy的newProxyInstance
创建动态代理对象
● 详细的代码:
☺(2) 动态代理原理:
- 原理和静态代理差不多【客户端直接使用的都是
代理对象
,并不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介作用
。通过代理对象间接的调用真实对象的方法】 - 只不过,
动态代理的代理类
,不是由我们所创建,是我们生成字节码对应格式和结构的二进制数据加载进虚拟机,动态生成的
。
● 详细的代码[通过 DynamicProxyClassGenerator 生成动态代理的字节码,再通过反编译工具查看。]:
(3) CGLIB动态代理:
- 第三方,需要拷贝jar包【spring-frame框架已经集成了cglib动态代理】
原理:继承
● 详细的代码:
(4)JDK动态代理和CGLIB动态代理的区别:
■ JDK动态代理:要求 真实对象 必须要实现接口
。
■ CGLIB动态代理:可以针对没有接口.
☺ 五、代理的总结:
1、代理原理图:
☺ 2、代理的目标/作用:为了给目标对象(真实对象)的方法做功能的增强
。
☺ 3、动态代理原理:
- 原理和静态代理差不多【客户端直接使用的都是
代理对象
,并不知道真实对象是谁,此时代理对象可以在客户端和真实对象之间起到中介作用
。通过代理对象间接的调用真实对象的方法】 - 只不过,
动态代理的代理类
,不是由我们所创建,是我们生成字节码对应格式和结构的二进制数据加载进虚拟机,动态生成的
。
4、JDK 动态代理 和 CGLIB 动态代理的总结:有接口-使用jdk,没有接口-使用cglib
(1) JDK 动态代理:
① JAVA 动态代理是使用 java.lang.reflect 包
中的 Proxy 类与 InvocationHandler 接口
这两个来完成的。
② 要使用 JDK 动态代理,委托类(真实类)必须要定义接口
。
③ JDK 动态代理将会拦截所有 pubic 的方法
(因为只能调用接口中定义的方法),这样即使在接口中增加 了新的方法,不用修改代码也会被拦截。
④ 动态代理的最小单位是类(所有类中的方法都会被处理
),如果只想拦截一部分方法,可以在 invoke 方法 中对要执行的方法名进行判断
[判断内容可以放到配置文件,方便后续修改和维护~]
(2) CGLIB 动态代理:
① CGLIB 可以生成委托类的子类,并重写父类非 final 修饰符的方法
。
② 要求类不能是 final 的,要拦截的方法要是非 final、非 static、非 private 的。
③ 动态代理的最小单位是类(所有类中的方法都会被处理);
5、性能和选择 [有接口-使用jdk,没有接口-使用cglib
+ 性能要求有要求-Javassit]
- JDK 动态代理是基于实现接口的,CGLIB 和 Javassit 是基于继承委托类的。
- 从性能上考虑:Javassit > CGLIB > JDK Struts2 的拦截器和 Hibernate 延迟加载对象,采用的是 Javassit 的方式.
- 对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统,也更
符合面向接口编程规范
。 - 若委托对象实现了干接口,优先选用 JDK 动态代理。 若委托对象没有实现任何接口,使用 Javassit 和 CGLIB 动态代
☺ 6、动态代理的应用:过滤器、拦截器、日志记录
1、过滤器Filter
2、拦截器Interceptor
- 过滤器和拦截器差不多,只是过滤器是针对与web领域的概念,只能针对与请求和响应做增强,离不开servlet-api.jar;而拦截器是对于整个java领域的概念,不仅可以应用到web层,还可以应用到service层。
3、日志记录log
作者:一乐乐