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

本文介绍了代理模式的基本概念,包括其在结构型模式中的作用,以及如何在不同场景下使用,如解决远程访问控制问题。详细讲解了静态代理和动态代理(JDK和CGLIB)的实现,并探讨了SpringAOP中代理的选择原则和优缺点。

一、定义

代理模式(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. 增加了系统的复杂度
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值