← Назад к вопросам

Какие знаешь виды прокси?

1.7 Middle🔥 201 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Виды прокси в Java

Proxy — это паттерн проектирования, который предоставляет заменитель или placeholder для другого объекта. Прокси позволяет контролировать доступ к объекту, отложить его создание, добавить дополнительную логику. Я активно использую прокси в различных сценариях.

1. Protection Proxy (Прокси защиты)

Контролирует доступ к объекту на основе прав доступа:

// Интерфейс оригинального объекта
public interface DataService {
  String getData();
  void updateData(String data);
}

// Реальная реализация
public class RealDataService implements DataService {
  
  @Override
  public String getData() {
    return "sensitive data";
  }
  
  @Override
  public void updateData(String data) {
    System.out.println("Updated: " + data);
  }
}

// Protection Proxy
public class ProtectionProxy implements DataService {
  
  private final DataService realService;
  private final User currentUser;
  
  public ProtectionProxy(DataService realService, User currentUser) {
    this.realService = realService;
    this.currentUser = currentUser;
  }
  
  @Override
  public String getData() {
    if (currentUser.hasPermission("READ")) {
      return realService.getData();
    }
    throw new AccessDeniedException("User has no READ permission");
  }
  
  @Override
  public void updateData(String data) {
    if (currentUser.hasPermission("WRITE")) {
      realService.updateData(data);
    } else {
      throw new AccessDeniedException("User has no WRITE permission");
    }
  }
}

// Использование
DataService service = new ProtectionProxy(
  new RealDataService(),
  currentUser
);

2. Virtual Proxy (Ленивое создание объекта)

Отлаживает создание дорогостоящего объекта до момента его использования:

public interface Image {
  void display();
}

public class RealImage implements Image {
  
  private String filename;
  
  public RealImage(String filename) {
    System.out.println("Loading image from disk: " + filename);
    this.filename = filename;
  }
  
  @Override
  public void display() {
    System.out.println("Displaying: " + filename);
  }
}

// Virtual Proxy — создаёт RealImage только при display()
public class ImageProxy implements Image {
  
  private String filename;
  private RealImage realImage;
  
  public ImageProxy(String filename) {
    this.filename = filename;  // Только сохраняем имя
  }
  
  @Override
  public void display() {
    if (realImage == null) {
      realImage = new RealImage(filename);  // Ленивое создание
    }
    realImage.display();
  }
}

// Использование
Image image = new ImageProxy("photo.jpg");
// RealImage ещё не загружена
image.display();  // Теперь загружается

3. Remote Proxy (Прокси удалённого объекта)

Представляет объект, находящийся на удалённой машине:

public interface UserService {
  User getUserById(Long id);
  void updateUser(User user);
}

// Прокси, который обращается к удалённому сервису
public class RemoteUserServiceProxy implements UserService {
  
  private final String remoteServiceUrl;
  private final RestTemplate restTemplate;
  
  public RemoteUserServiceProxy(String url, RestTemplate restTemplate) {
    this.remoteServiceUrl = url;
    this.restTemplate = restTemplate;
  }
  
  @Override
  public User getUserById(Long id) {
    try {
      String url = remoteServiceUrl + "/users/" + id;
      return restTemplate.getForObject(url, User.class);
    } catch (RestClientException e) {
      throw new ServiceUnavailableException("User service is unavailable", e);
    }
  }
  
  @Override
  public void updateUser(User user) {
    String url = remoteServiceUrl + "/users/" + user.getId();
    restTemplate.put(url, user);
  }
}

// Использование
UserService service = new RemoteUserServiceProxy(
  "http://remote-server:8080/api",
  new RestTemplate()
);
User user = service.getUserById(1L);

4. Logging/Caching Proxy

Добавляет логирование или кэширование к существующему объекту:

public class CachingCalculatorProxy implements Calculator {
  
  private final Calculator realCalculator;
  private final Map<String, Integer> cache = new HashMap<>();
  
  public CachingCalculatorProxy(Calculator realCalculator) {
    this.realCalculator = realCalculator;
  }
  
  @Override
  public int calculate(int a, int b, String operation) {
    String key = a + ":" + b + ":" + operation;
    
    // Проверка кэша
    if (cache.containsKey(key)) {
      System.out.println("Cache hit for " + key);
      return cache.get(key);
    }
    
    // Вычисление
    int result = realCalculator.calculate(a, b, operation);
    cache.put(key, result);
    return result;
  }
}

// Логирующий прокси
public class LoggingCalculatorProxy implements Calculator {
  
  private final Calculator realCalculator;
  private static final Logger logger = LoggerFactory.getLogger(LoggingCalculatorProxy.class);
  
  public LoggingCalculatorProxy(Calculator realCalculator) {
    this.realCalculator = realCalculator;
  }
  
  @Override
  public int calculate(int a, int b, String operation) {
    logger.info("Calculating: {} {} {}", a, operation, b);
    long startTime = System.currentTimeMillis();
    
    int result = realCalculator.calculate(a, b, operation);
    
    long duration = System.currentTimeMillis() - startTime;
    logger.info("Result: {}, Duration: {}ms", result, duration);
    return result;
  }
}

5. Dynamic Proxy (Динамический прокси)

Создание прокси в runtime с помощью Java Reflection API:

public class DynamicProxyExample {
  
  public static void main(String[] args) {
    UserService realService = new RealUserService();
    
    // Создание динамического прокси
    UserService proxyService = (UserService) Proxy.newProxyInstance(
      UserService.class.getClassLoader(),
      new Class[]{UserService.class},
      new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
          
          // До вызова
          System.out.println("Before: " + method.getName());
          long startTime = System.currentTimeMillis();
          
          // Вызов реального метода
          Object result = method.invoke(realService, args);
          
          // После вызова
          long duration = System.currentTimeMillis() - startTime;
          System.out.println("After: " + method.getName() + 
            " (took " + duration + "ms)");
          
          return result;
        }
      }
    );
    
    // Использование прокси
    proxyService.getUserById(1L);
  }
}

public interface UserService {
  User getUserById(Long id);
}

public class RealUserService implements UserService {
  @Override
  public User getUserById(Long id) {
    return new User(id, "John Doe");
  }
}

6. Spring AOP Proxy

Spring использует прокси для реализации AOP (Aspect-Oriented Programming):

// Aspect для логирования
@Aspect
@Component
public class LoggingAspect {
  
  @Around("execution(* com.example.service..*(..))") 
  public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
    long startTime = System.currentTimeMillis();
    System.out.println("Executing: " + joinPoint.getSignature().getName());
    
    Object result = joinPoint.proceed();  // Вызов реального метода
    
    long duration = System.currentTimeMillis() - startTime;
    System.out.println("Execution time: " + duration + "ms");
    
    return result;
  }
}

// Сервис
@Service
public class OrderService {
  
  // Spring автоматически создаст прокси
  public void createOrder(Order order) {
    System.out.println("Creating order...");
  }
}

7. Lazy Initialization Proxy

Для инициализации сложных зависимостей:

public class DatabaseProxy implements Database {
  
  private Database realDatabase;
  private String connectionString;
  
  public DatabaseProxy(String connectionString) {
    this.connectionString = connectionString;
    // БД ещё не инициализирована
  }
  
  private Database getDatabase() {
    if (realDatabase == null) {
      System.out.println("Initializing database connection...");
      realDatabase = new RealDatabase(connectionString);
    }
    return realDatabase;
  }
  
  @Override
  public ResultSet query(String sql) {
    return getDatabase().query(sql);
  }
}

8. Copy-On-Write Proxy

Для оптимизации памяти при копировании объектов:

public class CopyOnWriteDocument implements Document {
  
  private Document original;
  private Document copy;
  
  public CopyOnWriteDocument(Document original) {
    this.original = original;
  }
  
  @Override
  public String getContent() {
    // Просто читаем из оригинала
    return original.getContent();
  }
  
  @Override
  public void setContent(String content) {
    // Копируем только при модификации
    if (copy == null) {
      copy = new RealDocument(original.getContent());
    }
    copy.setContent(content);
  }
}

Сравнение видов прокси

ТипНазначениеКогда использовать
ProtectionКонтроль доступаЗащита конфиденциальных данных
VirtualЛенивое созданиеДорогостоящие объекты
RemoteУдалённые объектыРаспределённые системы
Logging/CachingДобавление логикиМониторинг, оптимизация
DynamicRuntime проксиКогда тип неизвестен заранее
Spring AOPAspect-orientedCross-cutting concerns
Copy-On-WriteОптимизация памятиМногопоточные сценарии

Мой практический опыт

  • Использовал Spring AOP для логирования и мониторинга в production системах
  • Реализовал Virtual Proxy для инициализации тяжёлых ресурсов
  • Применял Dynamic Proxy для generic обработки методов
  • Использовал Remote Proxy для интеграции микросервисов
  • Implement Caching Proxy для оптимизации производительности

Прокси — это мощный паттерн, который позволяет добавлять функциональность без изменения оригинального класса.