定义
代理模式(Proxy Pattern)的定义也非常简单,是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客服端和目标对象之间起到中介作用,代理模式属于结构型设计模式。
代理其实就是在原本逻辑前后增加一些逻辑,而调用者无感知
分类
-
静态代理
-
动态代理
-
jdk动态代理
-
cglib动态代理
-
目的
-
保护目标对象
-
增强目标对象
静态代理
场景
人到了适婚年龄,父母总是迫不及待希望早点抱孙子。而现在社会的人在各 种压力之下,都选择晚婚晚育。于是着急的父母就开始到处为自己的子女相亲,比子女自己还着急。这个相亲的过程,就是一种我们人人都有份的代理
顶层接口Person
public interface Person {
void findLove();
}
儿子要找对象,实现Son类
public class Son implements Person{
public void findLove(){
System.out.println("儿子要求:肤白貌美大长腿");
}
}
但是儿子比较忙去不了,只能父母帮忙去相亲了,创建father类
public class Father implements Person {
private Son person;
public Father(Son person){
this.person = person;
}
public void findLove(){
System.out.println("父亲物色对象");
this.person.findLove();
System.out.println("双方父母同意,确立关系");
}
}
测试类
Father father = new Father(new Son());
father.findLove();
动态代理
先看jdk代理的实现方式
我们还以找对象为例,但是这个时候需要将找对象扩展为一个产业,进而出现媒婆等更加复杂的业务场景
创建婚介所类
public class JDKMeipo implements InvocationHandler {
private Object target;
public Object getInstance(Object target) throws Exception{
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.target,args);
after();
return obj;
}
private void before(){
System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after(){
System.out.println("OK的话,准备办事");
}
}
假如是女孩找对象,创建类
public class Girl implements Person {
public void findLove() {
System.out.println("高富帅");
System.out.println("身高180cm");
System.out.println("有6块腹肌");
}
}
测试类
Object obj = new JDKMeipo().getInstance(new Girl());
obj.findLove();
CGLib
看下cglib找对象的实现
创建媒婆类
public class CGlibMeipo implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
//相当于Proxy,代理的工具类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
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 before(){
System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
System.out.println("开始物色");
}
private void after(){
System.out.println("OK的话,准备办事");
}
}
创建顾客类
public class Customer {
public void findLove(){
System.out.println("儿子要求:肤白貌美大长腿");
}
}
测试类
Customer obj = (Customer) new CGlibMeipo().getInstance(Customer.class);
System.out.println(obj);
obj.findLove();
CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现的动态代理
CGLib 和 JDK 动态代理对比
-
JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象。
-
JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
-
JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法, CGLib 执行效率更高。
CGLib 执行效率更高原因:
CGLib 动态代理执行代理方法效率之所以比 JDK 的高是因为 Cglib 采用了 FastClass 机 制,它的原理简单来说就是:为代理类和被代理类各生成一个 Class,这个 Class 会为代 理类或被代理类的方法分配一个 index(int 类型)。这个 index 当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK 动态代理通过反射调用高。
Spring中的代理选择原则
1、当 Bean 有实现接口时,Spring 就会用 JDK 的动态代理
2、当 Bean 没有实现接口时,Spring 选择 CGLib。
3、Spring 可以通过配置强制使用 CGLib,只需在 Spring 的配置文件中加入如下代码:
<aop:aspectj-autoproxy proxy-target-class="true"/>
总结:
静态代理和动态的本质区别
1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则。
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。
代理模式优点
1、代理模式能将代理对象与真实被调用的目标对象分离。
2、一定程度上降低了系统的耦合度,扩展性好。
3、可以起到保护目标对象的作用。
4、可以对目标对象的功能增强。
代理模式缺点
1、代理模式会造成系统设计中类的数量增加。
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
3、增加了系统的复杂度。