跳转到内容

Java代理模式

来自代码酷

Java代理模式[编辑 | 编辑源代码]

代理模式(Proxy Pattern)是一种结构型设计模式,它允许通过一个代理对象控制对另一个对象的访问。代理模式常用于延迟初始化、访问控制、日志记录等场景,是Java中常见的设计模式之一。

简介[编辑 | 编辑源代码]

代理模式的核心思想是为某个对象提供一个代理,以控制对该对象的访问。代理对象可以在客户端和目标对象之间起到中介的作用,从而在不修改目标对象代码的情况下增强其功能。

代理模式通常分为以下三种类型:

  • 静态代理:在编译时确定代理关系。
  • 动态代理:在运行时动态生成代理类。
  • 虚拟代理:延迟加载目标对象,直到真正需要时才创建。

静态代理[编辑 | 编辑源代码]

静态代理是最简单的代理实现方式,代理类和目标类实现相同的接口,并在代理类中调用目标类的方法。

示例代码[编辑 | 编辑源代码]

// 1. 定义接口
interface Image {
    void display();
}

// 2. 实现目标类
class RealImage implements Image {
    private String filename;

    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }

    private void loadFromDisk() {
        System.out.println("Loading image: " + filename);
    }

    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// 3. 实现代理类
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); // 延迟加载
        }
        realImage.display();
    }
}

// 4. 客户端代码
public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("test.jpg");
        
        // 第一次调用会加载图片
        image.display();
        
        // 第二次调用直接显示,无需重新加载
        image.display();
    }
}

输出结果[编辑 | 编辑源代码]

Loading image: test.jpg
Displaying image: test.jpg
Displaying image: test.jpg

解释[编辑 | 编辑源代码]

1. 定义`Image`接口,包含`display()`方法。 2. `RealImage`是实际加载和显示图片的类。 3. `ProxyImage`控制对`RealImage`的访问,实现了延迟加载功能。 4. 客户端通过代理访问图片,第一次调用时才会真正加载图片。

动态代理[编辑 | 编辑源代码]

Java动态代理利用反射机制在运行时动态创建代理类,比静态代理更灵活。主要通过`java.lang.reflect.Proxy`和`java.lang.reflect.InvocationHandler`实现。

示例代码[编辑 | 编辑源代码]

import java.lang.reflect.*;

// 1. 定义接口
interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
}

// 2. 实现目标类
class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }
}

// 3. 实现InvocationHandler
class LoggingHandler implements InvocationHandler {
    private Object target;

    public LoggingHandler(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. 客户端代码
public class DynamicProxyDemo {
    public static void main(String[] args) {
        Calculator realCalculator = new CalculatorImpl();
        
        Calculator proxy = (Calculator) Proxy.newProxyInstance(
            Calculator.class.getClassLoader(),
            new Class[]{Calculator.class},
            new LoggingHandler(realCalculator)
        );
        
        System.out.println("Result: " + proxy.add(5, 3));
        System.out.println("Result: " + proxy.subtract(5, 3));
    }
}

输出结果[编辑 | 编辑源代码]

Before method: add
After method: add
Result: 8
Before method: subtract
After method: subtract
Result: 2

解释[编辑 | 编辑源代码]

1. 定义`Calculator`接口和实现类`CalculatorImpl`。 2. `LoggingHandler`实现`InvocationHandler`,在方法调用前后添加日志。 3. 通过`Proxy.newProxyInstance()`动态创建代理对象。 4. 客户端调用代理对象的方法时,会自动调用`invoke()`方法。

代理模式类图[编辑 | 编辑源代码]

classDiagram class Subject { <<interface>> +request() } class RealSubject { +request() } class Proxy { -realSubject: RealSubject +request() } Subject <|.. RealSubject Subject <|.. Proxy Proxy --> RealSubject

应用场景[编辑 | 编辑源代码]

代理模式在以下场景中特别有用:

  • 远程代理:为远程对象提供本地代表(如RMI)。
  • 虚拟代理:延迟加载大资源对象(如图片、文件)。
  • 保护代理:控制对敏感对象的访问权限。
  • 智能引用:在访问对象时执行额外操作(如引用计数、线程安全检查)。

优缺点[编辑 | 编辑源代码]

优点[编辑 | 编辑源代码]

  • 职责清晰,符合单一职责原则。
  • 扩展性强,可以在不修改目标对象的情况下增强功能。
  • 客户端可以透明地使用代理对象。

缺点[编辑 | 编辑源代码]

  • 静态代理需要为每个服务创建代理类,工作量大。
  • 动态代理使用反射,性能略低于直接调用。
  • 增加了系统的复杂度。

进阶应用[编辑 | 编辑源代码]

Spring AOP中的代理[编辑 | 编辑源代码]

Spring框架广泛使用代理模式实现AOP(面向切面编程)。Spring AOP默认使用JDK动态代理(针对接口)或CGLIB(针对类)来创建代理对象。

示例:Spring AOP代理[编辑 | 编辑源代码]

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
    
    @After("execution(* com.example.service.*.*(..))")
    public void logAfter(JoinPoint joinPoint) {
        System.out.println("After method: " + joinPoint.getSignature().getName());
    }
}

性能考虑[编辑 | 编辑源代码]

对于性能敏感的场景,可以考虑:

  • 缓存代理对象,避免重复创建。
  • 使用字节码生成工具(如Byte Buddy)替代反射。
  • 在编译时生成代理(如Annotation Processing Tool)。

总结[编辑 | 编辑源代码]

代理模式是Java中强大的设计模式,它通过引入代理对象来控制对目标对象的访问。无论是简单的静态代理还是灵活的动态代理,都能有效地解耦客户端和目标对象,增强系统功能。理解代理模式对于掌握Java高级特性(如Spring AOP)至关重要。