一、定义
代理模式(Proxy Pattern)为其他对象提供一种代理以控制对这个对象的访问,属于结构型模式。
二、解决什么问题
主要解决在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
三、何时使用
想在访问一个类时做一些控制。
四、如何使用
增加中间层
核心思想:
1.提供一个抽象的行为(抽象类或者接口)
2.代理类和被代理对象都实现抽象的行为
3.代理类拥有被代理对象的引用
4.访问代理类
核心角色:
- 抽象行为类
- 代理类
- 被代理对象(具体行为的实现类)
五、静态代理
基础版实现
Image接口:
/*** <p>* 图片接口* </p>** @author shiqi* @version 1.0.0* @createTime 2023-12-29*/
public interface Image {/*** 显示方法*/void display();}
具体的RealImage对象
/*** <p>* 具体实现类* </p>** @author shiqi* @version 1.0.0* @createTime 2023-12-29*/
public class RealImage implements Image {private String fileName;public RealImage(String fileName){this.fileName = fileName;loadFromDisk(fileName);}@Overridepublic void display() {System.out.println("Displaying " + fileName);}private void loadFromDisk(String fileName){System.out.println("Loading " + fileName);}
}
代理对象ProxyImage
/*** <p>* 代理类,代理具体的RealImage对象,在访问RealImage对象时,通过代理类处理* </p>** @author shiqi* @version 1.0.0* @createTime 2023-12-29*/
public class ProxyImage implements Image {private RealImage realImage;private String fileName;public ProxyImage(String fileName) {this.fileName = fileName;}@Overridepublic void display() {if(realImage == null){realImage = new RealImage(fileName);}}
}
进阶版实现
在执行目标方法前后执行其他的业务逻辑
代理主题角色类ISubject
/*** <p>* 代理主题角色类* </p>** @author shiqi* @version 1.0.0* @createTime 2023-12-29*/
public interface ISubject {void request();
}
真实主题角色类RealSubject
/*** <p>* 真实主题角色类* </p>** @author shiqi* @version 1.0.0* @createTime 2023-12-29*/
public class RealSubject implements ISubject{@Overridepublic void request() {System.out.println("real service is called.");}
}
代理主题角色类ProxySubject
/*** <p>* 代理主题角色类* </p>** @author shiqi* @version 1.0.0* @createTime 2023-12-29*/
public class ProxySubject implements ISubject {private ISubject subject;public ProxySubject(ISubject subject) {this.subject = subject;}@Overridepublic void request() {before();subject.request();after();}private void before() {System.out.println("called request before.");}private void after() {System.out.println("called request after.");}}
测试客户端
/*** <p>* 测试客户端* </p>** @author shiqi* @version 1.0.0* @createTime 2023-12-29*/
public class ProxySubjectDemo {public static void main(String[] args) {ISubject subject = new ProxySubject(new RealSubject());subject.request();}
}
运行结果:
六、动态代理实现
JDK动态代理
我们用一个媒婆例子来实现
- 定义一个IPerson接口,提供寻找真爱的方法findLove
public interface IPerson {void findLove();}
- 定义具体的媒婆类JdkMeipo
/*** <p>* 具体的媒婆* </p>** @author shiqi* @version 1.0.0* @createTime 2023-10-27*/
public class JdkMeipo implements InvocationHandler {private IPerson target;public IPerson getInstance(IPerson target){this.target = target;Class<?> clazz = target.getClass();return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object result = method.invoke(this.target, args);after();return null;}private void after() {System.out.println("双方同意,开始交往");}private void before() {System.out.println("我是媒婆,已经收集到你的需求,开始物色");}
}
- 定义具体的要找对象的Person类
/*** <p>** </p>** @author shiqi* @version 1.0.0* @createTime 2023-10-27*/
public class Zhangsan implements IPerson{@Overridepublic void findLove() {System.out.println("张三要求:肤白貌美大长腿");}
}
- 创建客户端进行测试
/*** <p>* 测试客户端* </p>** @author shiqi* @version 1.0.0* @createTime 2023-10-27*/
public class Test {public static void main(String[] args) {JdkMeipo jdkMeipo=new JdkMeipo();Zhangsan zhangsan=(Zhangsan) jdkMeipo.getInstance(new Zhangsan());zhangsan.findLove();}}
运行结果:
Cglib动态代理
- 定义核心的媒婆类CglibMeipo
import java.lang.reflect.Method;/*** <p>* Cglib的媒婆类* </p>** @author shiqi* @version 1.0.0* @createTime 2023-10-27*/
public class CglibMeipo implements MethodInterceptor {private IPerson target;public Object getInstance(Class<?> clazz) throws Exception {//相当于Proxy,代理的工具类Enhancer enhancer = new Enhancer();enhancer.setSuperclass(clazz);enhancer.setCallback(this);return enhancer.create();}@Overridepublic 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的话,准备办事");}
}
- 定义测试客户端 CglibProxyTest
/*** <p>* 测试客户端* </p>** @author shiqi* @version 1.0.0* @createTime 2023-12-29*/
public class CglibProxyTest {public static void main(String[] args) throws Exception {CglibMeipo cglibMeipo = new CglibMeipo();Zhangsan zhangsan = (Zhangsan) cglibMeipo.getInstance(Zhangsan.class);zhangsan.findLove();}
}
Cglib动态代理和Jdk动态代理的区别
CglibProxy和JdkProxy都是Java中用于实现代理模式的两种不同的方式。它们之间的主要区别在于代理的实现方式和对代理目标的要求。
基于继承 vs 基于接口:
CglibProxy使用的是基于继承的代理方式。它通过创建目标类的子类来实现代理。
JdkProxy使用的是基于接口的代理方式。它要求目标类必须实现一个接口,然后通过动态生成实现该接口的代理类。
代理类的生成方式:
CglibProxy通过字节码操作,在运行时动态生成目标类的子类,继承目标类,并重写需要代理的方法。
JdkProxy使用Java的动态代理机制,通过java.lang.reflect.Proxy和InvocationHandler接口来生成代理类,该代理类实现了目标接口,并将方法调用委托给InvocationHandler的实现。
性能:
由于CglibProxy是通过生成目标类的子类,因此在性能上可能会比JdkProxy略慢,尤其是对于目标类的方法调用。
JdkProxy相对来说更轻量,因为它是基于接口的代理,不需要生成子类。
应用场景:
CglibProxy适用于那些没有实现接口的类的代理,因为它是基于继承的。
JdkProxy适用于那些实现了接口的类的代理,且它更符合Java的设计哲学,推崇面向接口编程。
七、代理模式在源码中的体现
- ProxyFactoryBean核心方法getObject (),源码如下:
- Spring AOP
Spring 利用动态代理实现AOP 时有两个非常重要的类:JdkDynamicAopProxy 类和C g l i b A o p P r o x y 类 ,来 看 一 下类 图 ,如 下 图 所 示 。
S p r i n g 中的代理选择原则
- 当 Bean有实现接口时 ,Spring就会用Jdk动态代理。
- 当Bean没有实现接又时,Spring 会选择CGLib代理。
- Spring可 以 通 过 配 置 强 制 使 用 CGLib代 理 ,只 需 在Spring的 配 置 文 件 中 加 入 如 下 代 码 : <aop:aspectj-autoproxy proxy-target-class=“true”/>
八、总结
代理模式的优缺点
优点
- 代理模式能将代理对象与真实被调用目标对象分离
- 在一定程度上降低了系统的耦合性,扩展性好
- 可以起到保护目标对象的作用
- 可以增强目标对象的功能 (代理模式核心的点)
缺点
- 代理模式会造成系统设计中类的数量增加。
- 在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢
- 增加了系统的复杂度