代理模式(Proxy Pattern)是指由于某些原因需要给某个对象提供一个代理以控制对该对象的访问,这时访问对象不适合或者不能直接访问目标对象,使用代理对象作为目标对象和访问对象之间的中介
现实中的例子
房屋中介,律师,婚姻介绍所等...
代理模式的结构与特点
代理模式的主要角色
1.抽象主题(Abstract Subject)角色,通过接口或者抽象类声明真实主题和代理对象实现的业务方法
2.真实主题(Real Subject)角色,实现了抽象主题中的业务方法,是代理对象所代表的真实对象,是最终要被应用的对象
3.代理(Proxy)角色,提供了与真实主题相同的接口,其内部含有对真实主题的引用,可以访问,控制,和扩展真实主题的功能
代理模式的优缺点
优点
1.代理对象在访问对象和目标对象直接建立一个中介的作用和保护目标对象的作用
2.带对象可以扩展目标对象的功能
3.代理模式将客户端和目标对象进行分离,在一定程度上降低了系统的耦合度
缺点
1.在客户端和目标对象之间建立了一个代理对象,会造成访问速度的变慢
2.增加了系统的复杂度
静态代理
创建抽象主题,提供真实主题和代理对象实现的业务方法
/**
* 抽象主题 声明真实主题和代理主题必须实现的接口
*/
public interface Business {
public void shoping();
}
创建真实主题,实现抽象主题的业务方法
/**
* 真实主题 实现抽象主图的方法,是代理对象所代表的真实对象
*/
public class Client implements Business{
@Override
public void shoping() {
System.out.println("用户买了一个刮胡刀");
}
}
创建代理类,实现抽象主题的的业务方法,并持有对真实主题的引用
/**
* 代理主题 快递类,实现抽象主题的方法,其内部持有真实主题的引用,可以扩展真实主题的功能
*/
public class ExpressProxy implements Business{
// 真实主题的引用
private Business client;
public ExpressProxy(Business client){
this.client = client;
}
@Override
public void shoping() {
before();
this.client.shoping();
after();
}
// 功能扩展
private void before(){
System.out.println("接到订单通知");
}
// 功能扩展
public void after(){
System.out.println("订单已经发货...");
}
}
测试
public static void main(String[] args) {
// 创建代理类,并传入真实主题的引用
Business business = new ExpressProxy(new Client());
business.shoping();
}
结果
接到订单通知
用户买了一个刮胡刀
订单已经发货...
静态代理类的弊端
1.目标类和代理类必须实现同一个接口
2.目标类增多代理类也会随之增多
动态代理
JDK 动态代理
创建 Persion,提供找对象的方法
/**
* 抽象主题 提供真实主题要实现的业务逻辑
*/
public interface IPersion {
public void findLove();
}
分别创建真实主题,ZhangSan,LiSi 类,实现抽象主题的方法
/**
* 真实主题 实现抽象主题的方法 是被代理的目标类
*/
public class ZhangSan implements IPersion{
@Override
public void findLove() {
System.out.println("要求:肤白貌美大长腿...");
}
}
/**
* 真实主题 实现抽象主题的方法 是最终被代理的类
*/
public class LiSi implements IPersion{
@Override
public void findLove() {
System.out.println("要求:学历高,颜值高,个字高...");
}
}
创建代理媒婆类,实现 JDK ,Invocationhandler 接口
/**
* jdk 代理类 是有目标对象的引用
*/
public class JDKMeiPo implements InvocationHandler {
// 目标对象
private Object target;
public Object getProxy(Object target){
this.target = target;
return Proxy.newProxyInstance(this.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 扩展方法
before();
method.invoke(this.target,args);
// 扩展方法
after();
return null;
}
// 扩展方法
private void after() {
System.out.println("如果合适的话,就结婚吧");
}
// 扩展方法
private void before() {
System.out.println("我是媒婆,开始我要给你找对象,现在已经确认要求");
System.out.println("开始物色...");
}
}
测试
public static void main(String[] args) {
System.out.println("===============给张三找对象================");
JDKMeiPo zsProxy = new JDKMeiPo();
IPersion zs = (IPersion) zsProxy.getProxy(new ZhangSan());
zs.findLove();
System.out.println("===============给李四找对象================");
JDKMeiPo lsProxy = new JDKMeiPo();
IPersion ls = (IPersion) lsProxy.getProxy(new LiSi());
ls.findLove();
}
结果
===============给张三找对象================
我是媒婆,开始我要给你找对象,现在已经确认要求
开始物色...
要求:肤白貌美大长腿...
如果合适的话,就结婚吧
===============给李四找对象================
我是媒婆,开始我要给你找对象,现在已经确认要求
开始物色...
要求:学历高,颜值高,个字高...
如果合适的话,就结婚吧
CGLib 动态代理
加入 CGlib jar 包
cglib
cglib
3.3.0
构建 CGLIB 类,实现 MethodInterceptor 中的接口
/**
* CJlib 动态代理
*/
public class CGLibMeiPo implements MethodInterceptor {
/**
* 生成Cglib代理对象
* Class 真实对象的class对象
* @return Cglib代理对象
*/
public Object getProxy(Class> clazz){
// Cglib 的增强类对象
Enhancer enhancer = new Enhancer();
// 设置增强的类型,真实类的类型
enhancer.setSuperclass(clazz);
// 定义代理逻辑为当前对象,要求当前对象实现 MethodInterceptor 中的接口
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
// 扩展方法
private void after() {
System.out.println("如果合适的话,就结婚吧");
}
// 扩展方法
private void before() {
System.out.println("我是媒婆,开始我要给你找对象,现在已经确认要求");
System.out.println("开始物色...");
}
}
测试结果和 jdk 动态代理一样,结果也一样
CGLIB 和 JDK 动态代理的对比
1.JDK 动态代理实现了被代理对象的接口,CGLIB 代理类继承了被代理对象
2.JDK 动态代理和 CGLIB 代理都在运行时期生成类的字节码,JDK动态代理直接写 Classs 字节码,CGLIB代理使用 ASM 框架写 Class 字节码,CGLIB 代理实现更复杂,生成的代理对象比 JDK 动态代理效率低
3.JDK 动态代理动用方法是通过反射机制调用的,CGLIB 代理是通过 FastClass 机制直接调用方法的,CGLIB 代理的执行效率更高