一、定义
Provide a surrogate or placeholder for another object to control access to it. (为其他对象提
一种代理以控制对这个对象的访问。)
代理模式也叫做委托模式 ,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。
二、UML示意图
- Subject抽象主题角色:可以是抽象类,也可以是接口,是一个普通业务类型的抽象定义。
- RealSubject具体主题角色:也叫做被委托角色、被代理角色。是业务逻辑的具体执行中。
- Proxy代理主题角色:也叫委托类、代理类。负责把所有抽象主题类定义的方法通过访问控制后,委托给真实主题角色实现
三、代码实现
-
抽象主题类,对于普通业务类型的抽象,可以根据业务类型来声明具体的方法
/** * 代理类抽象接口 */ public interface Subject { void request(); }
-
具体主题类,实现抽象接口中定义的方法,具体实现某一类业务
/** * 真实的被代理类 * 实现抽象的代理接口,实现具体的操作 */ public class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject is doing request"); } }
-
代理主题类,负责将请求进行访问控制后,委托给具体主题类进行执行
/** * 代理类 */ public class Proxy implements Subject { /** * 持有被代理对象的引用 */ private Subject subject; /** * 通过构造注入进行初始化 */ public Proxy(Subject subject) { this.subject = subject; } /** * 在调用被代理对象的方法前后实现访问控制 */ @Override public void request() { //前置增强 before(); //调用代理对象的方法 subject.request(); //后置增强 after(); } private void before() { System.out.println("proxy class can doing something here before access real subject"); } private void after() { System.out.println("proxy class can doing something here after access real subject"); } }
-
Client类,模拟访问请求,以及请求结果输出
public class Client { public static void main(String[] args) { Subject subject = new RealSubject(); Proxy proxy = new Proxy(subject); proxy.request(); } }
proxy class can doing something here before access real subject RealSubject is doing request proxy class can doing something here after access real subject
四、动态代理
以上便是代理模式的静态实现,从结果上来说,他达到了我们想要对某个类中的方法进行访问控制的需求。但是,当我们的需求有所变更的时候,依然想使用这个代理类来完成需求,例如有另一种类型的业务Subject2需要做类似的访问控制,那么我们就需要去调整代码,让代理类实现Subject2接口,并实现接口中的所有方法,那这样的话就违反了设计原则中的开闭原则,并且设计十分不优雅。
此时就需要用到动态代理。在动态代理中,代理主题类不需要去实现抽象主题类,便可以实现对目标类的代理。
动态代理是基于JDK中的API,java.lang.reflect.Proxy类,动态的在内存中构建代理对象,从而实现代理。
JDK动态代理
-
UML示意图
-
代理主题类,实现InvocationHandler接口,在创建代理对象时,将当前对象传入代理对象中
/** * 代理类 * 实现InvocationHandler接口,在创建代理对象时,将当前对象传入代理对象中 */ public class ProxyFactory implements InvocationHandler { /** * 持有被代理对象的引用 */ private Object object; /** * 通过构造注入进行初始化 */ public ProxyFactory(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object invoke = method.invoke(object, args); after(); return invoke; } public Object getProxyInstance() { return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this); } private void before() { System.out.println("proxy class can doing something here before access real subject"); } private void after() { System.out.println("proxy class can doing something here after access real subject"); } }
-
Client类,模拟访问请求,正常输出
public class Client { public static void main(String[] args) { Subject subject = new RealSubject(); Subject proxy = (Subject) new ProxyFactory(subject).getProxyInstance(); proxy.request(); } }
CGLIB代理
CGLIB底层是通过使用字节码处理框架ASM来转换字节码并生成新的代理类。
-
UML示意图
-
导包
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
-
被代理主题类,不需要实现接口
/** * 真实的被代理类 * 实现抽象的代理接口,实现具体的操作 */ public class RealSubject { public void request() { System.out.println("RealSubject is doing request"); } }
-
代理主题类,实现MethodInterceptor接口,在创建代理对象时,将当前对象传入代理对象中
/** * 代理类 * 实现MethodInterceptor接口,在创建代理对象时,将当前对象传入代理对象中 */ public class ProxyFactory implements MethodInterceptor { /** * 持有被代理对象的引用 */ private Object object; /** * 通过构造注入进行初始化 */ public ProxyFactory(Object object) { this.object = object; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object invoke = method.invoke(object, objects); after(); return invoke; } /** * 返回一个代理对象,是object对象的代理对象 */ public Object getProxyInstance() { //1.创建工具类 Enhancer enhancer = new Enhancer(); //2.设置父类 enhancer.setSuperclass(object.getClass()); //3.设置回调函数 enhancer.setCallback(this); //4.创建子类对象,即代理对象 return enhancer.create(); } private void before() { System.out.println("proxy class can doing something here before access real subject"); } private void after() { System.out.println("proxy class can doing something here after access real subject"); } }
-
Client类,模拟访问请求,正常输出
public class Client { public static void main(String[] args) { RealSubject subject = new RealSubject(); RealSubject proxy = (RealSubject) new ProxyFactory(subject).getProxyInstance(); proxy.request(); } }
-
注意:
- 代理的类不能为final,否则会报java.lang.IllegalArgumentException异常;
- 目标对象的方法如果为final/static,那么就不会被拦截进行访问控制。
五、小结
- 静态代理是代理模式的灵魂,能够充分演绎代理模式的含义,但在实际运用中可能存在违反开闭原则的问题;
- 在动态代理选择的时候,如果需要代理的目标对象需要实现接口,则用JDK代理;如果不需要实现接口,用CGLIB代理。
六、参考
- 《设计模式之禅》第2版