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, поэтому его должно быть легко импортировать и запускать как есть.