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

Как отключить версию CGLIB

2.0 Middle🔥 121 комментариев
#ООП#Основы Java

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

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

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

# Как отключить версию CGLIB

Что такое CGLIB

CGLIB (Code Generation Library) — это библиотека для динамического создания подклассов Java классов во время выполнения. Spring использует CGLIB для создания прокси-объектов при использовании:

  • Аннотаций @Transactional
  • @Cacheable
  • Аспектов (AOP)
  • @Lazy инъекции

Проблемы с CGLIB

  1. Производительность — создание подклассов требует времени
  2. Конструкторы — требует конструктора без параметров
  3. Отладка — сложнее отлаживать код
  4. Размер JAR — добавляет зависимость
  5. Версии Java — проблемы с новыми версиями Java

Решение: Переключение на JDK динамические прокси

Способ 1: application.properties

# Отключить CGLIB, использовать JDK прокси
spring.aop.proxy-target-class=false

Это настройка работает глобально для всего приложения.

Способ 2: application.yml

spring:
  aop:
    proxy-target-class: false

Способ 3: Java конфигурация

@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class AopConfig {
    // proxyTargetClass = false → используется JDK прокси
    // proxyTargetClass = true  → используется CGLIB
}

// Для транзакций
@Configuration
@EnableTransactionManagement(proxyTargetClass = false)
public class TransactionConfig {
    // proxyTargetClass = false → используется JDK прокси
}

// Для кэширования
@Configuration
@EnableCaching(proxyTargetClass = false)
public class CacheConfig {
    // proxyTargetClass = false → используется JDK прокси
}

Важное условие: интерфейсы

Для использования JDK прокси обязательно нужны интерфейсы:

Правильно (с интерфейсом)

// Интерфейс
public interface UserService {
    User getUser(Long id);
    void saveUser(User user);
}

// Реализация
@Service
public class UserServiceImpl implements UserService {
    
    @Override
    @Transactional
    public User getUser(Long id) {
        // Код метода
        return new User();
    }
    
    @Override
    @Transactional
    public void saveUser(User user) {
        // Код метода
    }
}

// Использование через интерфейс
@RestController
public class UserController {
    
    @Autowired
    private UserService userService;  // Через интерфейс!
    
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }
}

Здесь JDK прокси создает прокси-интерфейса UserService, а не подклассе UserServiceImpl.

Неправильно (без интерфейса)

// БЕЗ интерфейса
@Service
public class UserService {
    
    @Transactional
    public User getUser(Long id) {
        return new User();
    }
}

// При proxyTargetClass=false будет ошибка!
// Spring будет вынужден использовать CGLIB

Полный пример конфигурации

# application.yml
spring:
  aop:
    proxy-target-class: false  # Отключить CGLIB
  datasource:
    url: jdbc:postgresql://localhost/mydb
    username: user
    password: pass
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
  cache:
    type: simple  # или redis
// Java конфигурация
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@EnableTransactionManagement(proxyTargetClass = false)
@EnableCaching(proxyTargetClass = false)
public class AppConfig {
    // Все включенные функции будут использовать JDK прокси
}

// Сервис с интерфейсом
public interface OrderService {
    Order createOrder(Order order);
    Order getOrder(Long id);
    void deleteOrder(Long id);
}

@Service
public class OrderServiceImpl implements OrderService {
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Override
    @Transactional
    public Order createOrder(Order order) {
        return orderRepository.save(order);
    }
    
    @Override
    @Transactional(readOnly = true)
    @Cacheable(key = "#id", cacheNames = "orders")
    public Order getOrder(Long id) {
        return orderRepository.findById(id).orElse(null);
    }
    
    @Override
    @Transactional
    @CacheEvict(key = "#id", cacheNames = "orders")
    public void deleteOrder(Long id) {
        orderRepository.deleteById(id);
    }
}

// Контроллер использует интерфейс
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @Autowired
    private OrderService orderService;  // Через интерфейс
    
    @GetMapping("/{id}")
    public Order getOrder(@PathVariable Long id) {
        return orderService.getOrder(id);
    }
    
    @PostMapping
    public Order createOrder(@RequestBody Order order) {
        return orderService.createOrder(order);
    }
    
    @DeleteMapping("/{id}")
    public void deleteOrder(@PathVariable Long id) {
        orderService.deleteOrder(id);
    }
}

Различия между JDK прокси и CGLIB

ПараметрJDK ПроксиCGLIB
Требует интерфейсДаНет
Скорость созданияБыстрееМедленнее
Скорость вызоваМедленнееБыстрее
Размер памятиМеньшеБольше
СовместимостьВышеНиже
final методыПоддерживаютсяНе поддерживаются
private методыПоддерживаютсяНе поддерживаются

Когда использовать каждый

Используй JDK прокси (proxyTargetClass=false):

  • Если классы имеют интерфейсы
  • Если производительность критична при запуске
  • Если нужна максимальная совместимость
  • Если избегаешь final методов

Используй CGLIB (proxyTargetClass=true):

  • Если классы БЕЗ интерфейсов
  • Если вызовы методов критичны по скорости
  • Если старое приложение уже использует CGLIB

Проверка какой прокси используется

@Service
public class ProxyDetectionService {
    
    public String getProxyType() {
        Class<?> proxyClass = this.getClass();
        String className = proxyClass.getName();
        
        if (className.contains("EnhancerBySpringCGLIB")) {
            return "CGLIB Proxy";
        } else if (className.contains("Proxy")) {
            return "JDK Proxy";
        } else {
            return "No Proxy";
        }
    }
}

@Component
public class ProxyChecker {
    
    @Autowired
    private ProxyDetectionService service;
    
    @PostConstruct
    public void checkProxy() {
        System.out.println("Proxy Type: " + service.getProxyType());
        // Output: "Proxy Type: JDK Proxy" или "CGLIB Proxy"
    }
}

Отключение CGLIB для конкретного класса

Если нельзя добавить интерфейс, но нужно избежать CGLIB:

// Создать интерфейс только для этого класса
public interface UserAuthService {
    void authenticateUser(String username, String password);
}

@Service
public class UserAuthServiceImpl implements UserAuthService {
    
    @Override
    @Transactional
    public void authenticateUser(String username, String password) {
        // Логика
    }
}

// Теперь можно использовать JDK прокси
private UserAuthService authService;  // Через интерфейс

Миграция с CGLIB на JDK прокси: чеклист

✓ Создать интерфейсы для всех сервис-классов ✓ Убедиться что методы не final ✓ Убедиться что не используются private методы с аннотациями ✓ Обновить инъекции: использовать интерфейсы вместо классов ✓ Установить spring.aop.proxy-target-class=false ✓ Запустить тесты и проверить функциональность ✓ Мониторить производительность при запуске

Отключение CGLIB улучшает совместимость, но требует использования интерфейсов.

Как отключить версию CGLIB | PrepBro