隐身之术:深入解析代理模式的神秘力量

一、定义

代理模式(Proxy Pattern)为其他对象提供一种代理以控制对这个对象的访问,属于结构型模式。

二、解决什么问题

主要解决在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

三、何时使用

想在访问一个类时做一些控制。

四、如何使用

增加中间层

核心思想:
1.提供一个抽象的行为(抽象类或者接口)
2.代理类和被代理对象都实现抽象的行为
3.代理类拥有被代理对象的引用
4.访问代理类

核心角色:

  1. 抽象行为类
  2. 代理类
  3. 被代理对象(具体行为的实现类)
    隐身之术:深入解析代理模式的神秘力量_第1张图片

五、静态代理

基础版实现

Image接口:

/**
 * 

* 图片接口 *

* * @author shiqi * @version 1.0.0 * @createTime 2023-12-29 */
public interface Image { /** * 显示方法 */ void display(); }

具体的RealImage对象

/**
 * 

* 具体实现类 *

* * @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); } @Override public void display() { System.out.println("Displaying " + fileName); } private void loadFromDisk(String fileName){ System.out.println("Loading " + fileName); } }

代理对象ProxyImage

/**
 * 

* 代理类,代理具体的RealImage对象,在访问RealImage对象时,通过代理类处理 *

* * @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; } @Override public void display() { if(realImage == null){ realImage = new RealImage(fileName); } } }

进阶版实现

在执行目标方法前后执行其他的业务逻辑

代理主题角色类ISubject

/**
 * 

* 代理主题角色类 *

* * @author shiqi * @version 1.0.0 * @createTime 2023-12-29 */
public interface ISubject { void request(); }

真实主题角色类RealSubject

/**
 * 

* 真实主题角色类 *

* * @author shiqi * @version 1.0.0 * @createTime 2023-12-29 */
public class RealSubject implements ISubject{ @Override public void request() { System.out.println("real service is called."); } }

代理主题角色类ProxySubject

/**
 * 

* 代理主题角色类 *

* * @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; } @Override public void request() { before(); subject.request(); after(); } private void before() { System.out.println("called request before."); } private void after() { System.out.println("called request after."); } }

测试客户端


/**
 * 

* 测试客户端 *

* * @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(); } }

运行结果:
隐身之术:深入解析代理模式的神秘力量_第2张图片

六、动态代理实现

JDK动态代理

我们用一个媒婆例子来实现

  1. 定义一个IPerson接口,提供寻找真爱的方法findLove
public interface IPerson {

    void findLove();

}
  1. 定义具体的媒婆类JdkMeipo
/**
 * 

* 具体的媒婆 *

* * @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); } @Override public 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("我是媒婆,已经收集到你的需求,开始物色"); } }
  1. 定义具体的要找对象的Person类
/**
 * 

* *

* * @author shiqi * @version 1.0.0 * @createTime 2023-10-27 */
public class Zhangsan implements IPerson{ @Override public void findLove() { System.out.println("张三要求:肤白貌美大长腿"); } }
  1. 创建客户端进行测试
/**
 * 

* 测试客户端 *

* * @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(); } }

运行结果:

隐身之术:深入解析代理模式的神秘力量_第3张图片

Cglib动态代理

  1. 定义核心的媒婆类CglibMeipo
import java.lang.reflect.Method;

/**
 * 

* Cglib的媒婆类 *

* * @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(); } @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("我是媒婆,我要给你找对象,现在已经确认你的需求"); System.out.println("开始物色"); } private void after(){ System.out.println("OK的话,准备办事"); } }
  1. 定义测试客户端 CglibProxyTest
/**
 * 

* 测试客户端 *

* * @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的设计哲学,推崇面向接口编程。

七、代理模式在源码中的体现

  1. ProxyFactoryBean核心方法getObject (),源码如下:
    隐身之术:深入解析代理模式的神秘力量_第4张图片
  2. Spring AOP
    Spring 利用动态代理实现AOP 时有两个非常重要的类:JdkDynamicAopProxy 类和C g l i b A o p P r o x y 类 ,来 看 一 下类 图 ,如 下 图 所 示 。
    隐身之术:深入解析代理模式的神秘力量_第5张图片

S p r i n g 中的代理选择原则

  1. 当 Bean有实现接口时 ,Spring就会用Jdk动态代理。
  2. 当Bean没有实现接又时,Spring 会选择CGLib代理。
  3. Spring可 以 通 过 配 置 强 制 使 用 CGLib代 理 ,只 需 在Spring的 配 置 文 件 中 加 入 如 下 代 码 :

八、总结

代理模式的优缺点

优点

  1. 代理模式能将代理对象与真实被调用目标对象分离
  2. 在一定程度上降低了系统的耦合性,扩展性好
  3. 可以起到保护目标对象的作用
  4. 可以增强目标对象的功能 (代理模式核心的点)

缺点

  1. 代理模式会造成系统设计中类的数量增加。
  2. 在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢
  3. 增加了系统的复杂度

你可能感兴趣的:(设计模式,代理模式)