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

发布时间:2023年12月29日

一、定义

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

二、解决什么问题

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

三、何时使用

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

四、如何使用

增加中间层

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

核心角色:

  1. 抽象行为类
  2. 代理类
  3. 被代理对象(具体行为的实现类)
    在这里插入图片描述

五、静态代理

基础版实现

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);
    }


    @Override
    public 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;
    }
    
    @Override
    public 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{
    @Override
    public 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;
    }
    
    @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.");
    }

}

测试客户端


/**
 * <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动态代理

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

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

    void findLove();

}
  1. 定义具体的媒婆类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);
    }

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

  1. 定义核心的媒婆类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();
    }

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

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

  1. ProxyFactoryBean核心方法getObject (),源码如下:
    在这里插入图片描述
  2. Spring AOP
    Spring 利用动态代理实现AOP 时有两个非常重要的类:JdkDynamicAopProxy 类和C g l i b A o p P r o x y 类 ,来 看 一 下类 图 ,如 下 图 所 示 。
    在这里插入图片描述

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

  1. 当 Bean有实现接口时 ,Spring就会用Jdk动态代理。
  2. 当Bean没有实现接又时,Spring 会选择CGLib代理。
  3. Spring可 以 通 过 配 置 强 制 使 用 CGLib代 理 ,只 需 在Spring的 配 置 文 件 中 加 入 如 下 代 码 : <aop:aspectj-autoproxy proxy-target-class=“true”/>

八、总结

代理模式的优缺点

优点

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

缺点

  1. 代理模式会造成系统设计中类的数量增加。
  2. 在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢
  3. 增加了系统的复杂度
文章来源:https://blog.csdn.net/Hi_alan/article/details/135290146
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。