目录
一、介绍
1. 概念
代理模式也称为委托模式,属于结构型设计模式之一。
2. 产生的原因
在某些情况下,一个对象不适合或者不能直接引用另外一个对象,需要一个中间对象来进行间接的访问,这个中间对象就是代理对象。代理对象可以在客户端和目标对象之间起到中介的作用,比如我们日常生活中的邮局,快递公司,婚介所等等。
3. 使用的目的
代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。
4. 分类
代理模式分为静态代理模式和动态代理模式。
静态代理
是由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行之前,代理类.class文件就已经被创建了。属于编译期的代理。
动态代理
是在程序运行时通过java反射机制动态创建的。属于运行时的代理。
二、UML类图
三、静态代理
1. 场景举例
静态代理模式的话我模拟一个古代结婚的场景。在古代,某家的公子看上了别家的姑娘,一般都是家里的大人去姑娘的家里提亲,双方父母同意了,然后就拜堂成婚,后面要宴请亲朋好友。这里这个公子只需要拜堂成婚就行了,至于提亲和宴请亲友都是父母操办的。我们用代码来模拟一下这个场景。
(1)首先我们来建个 Person
抽象类:
public abstract class Person {
abstract void marry();
}
(2)然后这家公子要成亲,我们建个 Son
类来继承 Person
类:
public class Son extends Person {
@Override
public void marry() {
System.out.println("我终于结婚了");
}
}
(3)父亲帮儿子提亲,建个 Father
类,同样继承Person抽象类:
public class Father extens Person {
private Son son;
public Father(Son son){
this.son = son;
}
@Override
public void marry(){
System.out.println("父亲上门提亲");
son.marry();
System.out.println("父亲宴请亲友");
}
}
(4)测试类
public class Test {
public static void main(String[] args) {
Father father = new Father(new Son());
father.marry();
}
}
(5)输出
父亲上门提亲
我终于结婚了
父亲宴请亲友
2. 总结
优点:
代理类接受一个Subject接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。
缺点:
每一个代理类都必须实现一遍委托类(也就是RealSubject)的接口,如果接口增加方法,则代理类也必须跟着修改;
其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类将会非常臃肿,并且违背了单一职责的原则。
基于以上的缺点,于是出现了动态代理。
四、动态代理
1. 场景举例
动态代理模式分为 JDK
动态代理和 cglib
动态代理两种。这里先用 JDK
动态代理的方式来模拟一个通过婚介所找朋友的场景。
(1)先定义一个Person接口
public interface Person {
/**
* 找朋友
*/
void findFriend();
}
(2)然后创建婚介所 JDKMatrimonialAgency
类
public class JDKMatrimonialAgency 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("这里是婚介所,请提供你的需求");
}
private void after(){
System.out.println("已经找到合适的,尽快安排你相亲");
}
}
JDK
动态代理主要是实现 InvocationHandler
接口,并实现 invoke
方法
(3)然后创建 Customer
类
public class Customer implements Person {
@Override
public void findFriend() {
System.out.println("我要找一个腿长又好看的美女");
}
}
(4)测试类
public class Test {
public static void main(String[] args) {
try {
Person obj = (Person)new JDKMatrimonialAgency().getInstance(new Customer());
obj.findFriend();
} catch (Exception e) {
e.printStackTrace();
}
}
}
(5)输出结果
这里是婚介所,请提供你的需求
我要找一个胸大,腿长又好看的美女
已经找到合适的,尽快安排你相亲
接下来,我们用 CGLIB
来实现。
创建动态代理类CglibMatrimonialAgency:
public class CglibMatrimonialAgency implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
Enhancer enhancer = new Enhancer();
//要把哪个设置为即将生成的新类的父类,一般就是被代理的类
enhancer.setSuperclass(clazz);
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 before(){
System.out.println("这里是婚介所,请提供你的需求");
}
private void after(){
System.out.println("已经找到合适的,尽快安排你相亲");
}
}
CGLIB
主要是实现 MethodInterceptor
并重写 intercept
方法。
输出结果:
这里是婚介所,请提供你的需求
我要找一个胸大,腿长又好看的美女
已经找到合适的,尽快安排你相亲
2. JDK和CGLIB动态代理对比
(1)JDK
动态代理是实现了被代理对象的接口,CGLib
是继承了被代理对象。
(2)JDK
和 CGLib
都是在运行时生成字节码,JDK
是直接写 Class
字节码,而CGLib
使用 ASM
框架间接写 Class
字节码,Cglib
代理实现更加复杂,生成代理类的效率比 JDK
低。
(3)JDK
调用代理类的方法,是通过反射机制调用,而CGLib
是通过 FastClass
机制直接调用目标类的方法, 因此CGLib
执行效率更高。
五、代理模式的优缺点
优点:
- 降低耦合度,扩展性好
- 代理对象能起到保护目标对象的作用
- 可以实现对目标对象的功能增强
缺点:
- 增加了类的数量
- 因为会调用类的增强方法,所以会造成处理速度变慢
- 增加了系统的复杂度(这是好的架构都会有的缺点,比如spring)
参考文件:
《大话设计模式》《Head First》