目录

Java-代理模式从静态代理到动态代理

/weixin_miniapp.png

Java 代理模式:从静态代理到动态代理

前言

代理模式

是 Java 中常见的设计模式之一,它的核心思想是通过一个

代理对象

来控制对真实对象的访问。代理模式不仅可以扩展目标对象的功能,而且在不修改原目标对象的情况下,可以增加一些我们自定义的操作。

https://i-blog.csdnimg.cn/direct/d85a6c2e137b4b40bb50591bfc7a3821.png

1. 代理模式简介

代理模式的

核心思想

是: 通过代理对象来代替对真实对象的访问 。这样做的好处是,我们可以在不修改原目标对象的前提下,扩展目标对象的功能。比如,在目标对象的某个方法执行前后,你可以增加一些自定义的操作。

举个例子:

新娘找来了自己的闺蜜来代替自己处理新郎的提问。新娘收到的提问都是经过闺蜜处理过滤之后的。闺蜜在这里就可以看作是新娘的代理对象,代理的行为(方法)是接收和回复新郎的提问。

再举个例子:

你是一个明星,每天有很多粉丝找你签名。但你是大忙人,没时间亲自处理这些请求,所以你找了一个经纪人(代理对象)来帮你处理。粉丝的请求先交给经纪人,经纪人再决定是否转交给你,或者帮你做一些额外的处理(比如记录谁来找你签名)。这样,你既不用亲自处理所有请求,又能保证签名工作顺利进行。

2. 静态代理

2.1 什么是静态代理?

静态代理是指,

在编译时就已经确定了代理类和目标类的关系

。我们需要手动为每个目标类创建一个代理类,并在代理类中调用目标类的方法。

静态代理的缺点: 灵活性较差,一旦接口新增方法,目标类和代理类都需要进行修改。

2.2 静态代理的实现步骤

  1. 定义一个接口及其实现类 ;
  2. 创建一个代理类并实现该接口 ;
  3. 将目标对象注入代理类,并在代理类的方法中调用目标类的方法 。

2.3 代码示例

1. 定义发送短信的接口
public interface SmsService {
    String send(String message);
}
2. 实现发送短信的接口
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
3. 创建代理类并实现发送短信的接口
public class SmsProxy implements SmsService {

    private final SmsService smsService;

    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        // 调用方法之前,我们可以添加自己的操作
        System.out.println("before method send()");
        smsService.send(message);
        // 调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method send()");
        return null;
    }
}
4. 实际使用
public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

运行上述代码之后,控制台打印出:

before method send()
send message:java
after method send()

可以看到,我们成功地在 SmsServiceImpl 的 send() 方法前后增加了自定义操作。

3. 动态代理

3.1 什么是动态代理?

相比于静态代理, 动态代理更加灵活 。我们不需要针对每个目标类都单独创建一个代理类,而是可以在运行时动态生成代理类。动态代理的实现方式有很多种,比如 JDK 动态代理和 CGLIB 动态代理。

3.2 JDK 动态代理

3.2.1 JDK 动态代理的核心

在 JDK 动态代理中, InvocationHandler 接口和 Proxy 类是核心。 Proxy 类的 newProxyInstance() 方法用于生成代理对象,而 InvocationHandler 接口的 invoke() 方法则用于处理代理对象的方法调用。

3.2.2 JDK 动态代理的实现步骤
  1. 定义一个接口及其实现类 ;
  2. 自定义 InvocationHandler 并重写 invoke 方法 ;
  3. 通过 Proxy.newProxyInstance() 方法创建代理对象 。
3.2.3 代码示例
1. 定义发送短信的接口
public interface SmsService {
    String send(String message);
}
2. 实现发送短信的接口
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
3. 定义一个 JDK 动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DebugInvocationHandler implements InvocationHandler {
    private final Object target;

    public DebugInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("after method " + method.getName());
        return result;
    }
}
4. 获取代理对象的工厂类
import java.lang.reflect.Proxy;

public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new DebugInvocationHandler(target)
        );
    }
}
5. 实际使用
public class Main {
    public static void main(String[] args) {
        SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
        smsService.send("java");
    }
}

运行上述代码之后,控制台打印出:

before method send
send message:java
after method send

3.3 CGLIB 动态代理

3.3.1 CGLIB 动态代理的核心

CGLIB 动态代理通过继承目标类来生成代理类,因此它可以代理未实现任何接口的类。CGLIB 的核心是 MethodInterceptor 接口和 Enhancer 类。

3.3.2 CGLIB 动态代理的实现步骤
  1. 定义一个类 ;
  2. 自定义 MethodInterceptor 并重写 intercept 方法 ;
  3. 通过 Enhancer 类的 create() 方法创建代理类 。
3.3.3 代码示例
1. 实现一个使用阿里云发送短信的类
public class AliSmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
2. 自定义 MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class DebugMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method " + method.getName());
        Object object = methodProxy.invokeSuper(o, args);
        System.out.println("after method " + method.getName());
        return object;
    }
}
3. 获取代理类
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyFactory {
    public static Object getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(clazz.getClassLoader());
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new DebugMethodInterceptor());
        return enhancer.create();
    }
}
4. 实际使用
public class Main {
    public static void main(String[] args) {
        AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
        aliSmsService.send("java");
    }
}

运行上述代码之后,控制台打印出:

before method send
send message:java
after method send

4. 静态代理和动态代理的对比

  • 灵活性

    :动态代理更加灵活,不需要针对每个目标类都创建一个代理类,且可以直接代理实现类。

  • JVM 层面

    :静态代理在编译时生成 class 文件,而动态代理在运行时动态生成类字节码。

/weixin_miniapp.png