Рубрики
Без рубрики

Введение в cglib

Обзор cglib, популярной библиотеки генерации кода, ее функций и наиболее распространенных вариантов использования

Автор оригинала: baeldung.

1. Обзор

В этой статье мы рассмотрим библиотеку cglib (Библиотека генерации кода). Это библиотека байтового инструментария, используемая во многих Java-фреймворках, таких как Hibernate или Spring . Инструментарий байт-кода позволяет управлять или создавать классы после этапа компиляции программы.

2. Зависимость от Maven

Чтобы использовать cglib в своем проекте, просто добавьте зависимость Maven (последнюю версию можно найти здесь ):


    cglib
    cglib
    3.2.4

3. Cglib

Классы в Java загружаются динамически во время выполнения. Cglib использует эту функцию языка Java, чтобы сделать возможным добавление новых классов в уже запущенную программу Java.

Hibernate использует cglib для генерации динамических прокси-серверов. Например, он не вернет полный объект, хранящийся в базе данных, но вернет инструментальную версию сохраненного класса, который лениво загружает значения из базы данных по требованию.

Популярные фреймворки для насмешек, такие как Mockito, используют cglib для методов насмешек. Макет-это инструментальный класс, в котором методы заменяются пустыми реализациями.

Мы рассмотрим наиболее полезные конструкции из cglib.

4. Реализация Прокси-Сервера С Использованием cglib

Допустим, у нас есть класс PersonService , который имеет два метода:

public class PersonService {
    public String sayHello(String name) {
        return "Hello " + name;
    }

    public Integer lengthOfName(String name) {
        return name.length();
    }
}

Обратите внимание, что первый метод возвращает Строку , а второй Целое число.

4.1. Возврат того же значения

Мы хотим создать простой прокси-класс, который будет перехватывать вызов метода sayHello () . Класс Enhancer позволяет нам создавать прокси-сервер путем динамического расширения класса Person Service с помощью метода setSuperclass() из класса Enhancer :

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((FixedValue) () -> "Hello Tom!");
PersonService proxy = (PersonService) enhancer.create();

String res = proxy.sayHello(null);

assertEquals("Hello Tom!", res);

Фиксированное значение – это интерфейс обратного вызова, который просто возвращает значение из проксированного метода. Выполнение метода sayHello() на прокси-сервере вернуло значение, указанное в методе прокси.

4.2. Возвращаемое значение В зависимости от сигнатуры метода

Первая версия нашего прокси-сервера имеет некоторые недостатки, поскольку мы не можем решить, какой метод должен перехватывать прокси-сервер и какой метод следует вызывать из суперкласса. Мы можем использовать интерфейс MethodInterceptor для перехвата всех вызовов прокси-сервера и принятия решения о том, хотите ли вы выполнить конкретный вызов или выполнить метод из суперкласса:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
        return "Hello Tom!";
    } else {
        return proxy.invokeSuper(obj, args);
    }
});

PersonService proxy = (PersonService) enhancer.create();

assertEquals("Hello Tom!", proxy.sayHello(null));
int lengthOfName = proxy.lengthOfName("Mary");
 
assertEquals(4, lengthOfName);

В этом примере мы перехватываем все вызовы, когда сигнатура метода не принадлежит классу Object , что означает, что т. е. toString() или Хэш-код() методы не будут перехвачены. Кроме того, мы перехватываем только методы из службы Person , которая возвращает Строку . Вызов метода length Of Name() не будет перехвачен, поскольку его возвращаемый тип – Целое число.

5. Создатель Бобов

Еще одной полезной конструкцией из cglib является Генератор компонентов класса. Это позволяет нам динамически создавать компоненты и добавлять поля вместе с методами setter и getter. Он может быть использован инструментами генерации кода для создания простых объектов POJO:

BeanGenerator beanGenerator = new BeanGenerator();

beanGenerator.addProperty("name", String.class);
Object myBean = beanGenerator.create();
Method setter = myBean.getClass().getMethod("setName", String.class);
setter.invoke(myBean, "some string value set by a cglib");

Method getter = myBean.getClass().getMethod("getName");
assertEquals("some string value set by a cglib", getter.invoke(myBean));

6. Создание Микширования

A mixin – это конструкция, которая позволяет объединять несколько объектов в один. Мы можем включить поведение нескольких классов и представить это поведение как один класс или интерфейс. Миксины cglib позволяют объединять несколько объектов в один объект. Однако для этого все объекты, включенные в микширование, должны быть подкреплены интерфейсами.

Допустим, мы хотим создать смешение двух интерфейсов. Нам нужно определить как интерфейсы, так и их реализации:

public interface Interface1 {
    String first();
}

public interface Interface2 {
    String second();
}

public class Class1 implements Interface1 {
    @Override
    public String first() {
        return "first behaviour";
    }
}

public class Class2 implements Interface2 {
    @Override
    public String second() {
        return "second behaviour";
    }
}

Чтобы создать реализации Interface1 и Interface2 , нам нужно создать интерфейс, который расширяет их оба:

public interface MixinInterface extends Interface1, Interface2 { }

Используя метод create() из класса Mixin , мы можем включить поведение Class1 и Class2 в MixinInterface:

Mixin mixin = Mixin.create(
  new Class[]{ Interface1.class, Interface2.class, MixinInterface.class },
  new Object[]{ new Class1(), new Class2() }
);
MixinInterface mixinDelegate = (MixinInterface) mixin;

assertEquals("first behaviour", mixinDelegate.first());
assertEquals("second behaviour", mixinDelegate.second());

Вызов методов в mix в делегате вызовет реализации из Class1 и Class2.

7. Заключение

В этой статье мы рассмотрели cglib и его наиболее полезные конструкции. Мы создали прокси-сервер, используя класс Enhancer . Мы использовали Быть Создателем и, наконец, мы создали Миксин , который включал поведение других классов.

Cglib широко используется фреймворком Spring. Одним из примеров использования прокси-сервера cglib Весной является добавление ограничений безопасности к вызовам методов. Вместо прямого вызова метода Spring security сначала проверит (через прокси-сервер), прошла ли указанная проверка безопасности, и делегирует фактический метод только в том случае, если эта проверка прошла успешно. В этой статье мы рассмотрели, как создать такой прокси для наших собственных целей.

Реализацию всех этих примеров и фрагментов кода можно найти в проекте GitHub – это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.