Plastic — это встроенная в Tapestry библиотека Aspect Oriented Programming , которая в основном работает на уровне байт-кода, но защищает вас от большинства представлений на уровне байт-кода: обычно ваш код реализован с точки зрения вызова методов или чтения полей и записи в обратный вызов объекты, которые действуют как делегаты или фильтры.
Иногда, однако, вам нужно получить немного более низкий уровень и более непосредственно генерировать реализацию метода. Пластмасса также имеет свободный интерфейс для этого: InstructionBuilder .
Это пример из кода контейнера Tapestry Inversion Of Control (IoC); Экземпляр прокси — это то, что доступно другим сервисам, и включает в себя две конкретные проблемы: во-первых, поздняя реализация фактической реализации сервиса и, во-вторых, возможность сериализации прокси-объекта (даже если сервисы и другие объекты явно не сериализуемы ).
С точки зрения сериализации, что на самом деле сериализуется, это объект ServiceProxyToken; Когда впоследствии ServiceProxyToken будет десериализован, он может сослаться на эквивалентный прокси-объект в новой JVM и реестре служб IoC. Хитрость заключается в том, чтобы использовать магический метод writeReplace (), чтобы при сериализации прокси вместо этого записывался токен. Вот код:
private Object createProxyInstance(final ObjectCreator creator, final ServiceProxyToken token, final Class serviceInterface, final String description) { ClassInstantiator instantiator = proxyFactory.createProxy(serviceInterface, new PlasticClassTransformer() { public void transform(final PlasticClass plasticClass) { plasticClass.introduceInterface(Serializable.class); final PlasticField creatorField = plasticClass.introduceField(ObjectCreator.class, "creator").inject( creator); final PlasticField tokenField = plasticClass.introduceField(ServiceProxyToken.class, "token").inject( token); PlasticMethod delegateMethod = plasticClass.introducePrivateMethod(serviceInterface.getName(), "delegate", null, null); // If not concerned with efficiency, this might be done with method advice instead. delegateMethod.changeImplementation(new InstructionBuilderCallback() { public void doBuild(InstructionBuilder builder) { builder.loadThis().getField(creatorField); builder.invoke(ObjectCreator.class, Object.class, "createObject").checkcast(serviceInterface) .returnResult(); } }); for (Method m : serviceInterface.getMethods()) { plasticClass.introduceMethod(m).delegateTo(delegateMethod); } plasticClass.introduceMethod(WRITE_REPLACE).changeImplementation(new InstructionBuilderCallback() { public void doBuild(InstructionBuilder builder) { builder.loadThis().getField(tokenField).returnResult(); } }); plasticClass.addToString(description); } }); return instantiator.newInstance(); }
Для начала мы используем сервис PlasticProxyFactory для создания прокси, который реализует интерфейс сервиса.
Обратный вызов, переданный createProxy (), передается объекту PlasticClass . Первоначально это реализация интерфейса службы, где каждый метод интерфейса ничего не делает.
Базовая настройка включает создание прокси-сервера для реализации Serializable, а также создание и добавление значений в новые поля для других необходимых данных.
Затем создается метод под названием делегат (); он отвечает за ленивое создание реального сервиса при первой необходимости. Это на самом деле инкапсулировано внутри экземпляра ObjectCreator ; метод делегата () просто вызывает метод create () и передает результат в интерфейс службы.
Методы InstructionBuilder очень близко соответствуют байтовым кодам JVM. Так, например, загрузка поля экземпляра предполагает, что объект, содержащий поле, находится в стеке (через loadThis ()), затем использует значение this и заменяет его значением поля экземпляра в стеке, что требует знания класса. имя, имя поля и тип поля загружаемого поля. К счастью, PlasticField знает всю эту информацию, которая упрощает код.
Как только ObjectCreator окажется в стеке, метод для него может быть вызван; на уровне байтового кода это требует имени класса для класса, содержащего метод, возвращаемого типа метода и имени метода (и, для методов с параметрами, типов параметров). Результатом этого является экземпляр реализации службы, который приводится к типу интерфейса службы и возвращается.
Теперь, когда метод делегата () установлен, пришло время вызывать каждый метод на прокси, вызывать делегат (), а затем повторно вызывать метод в реализации службы с поздним созданием экземпляра. Поскольку этот тип делегирования очень распространен, он поддерживается методом DelegateTo ().
вводитьMethod () может получить доступ к существующему методу или создать новый; для метода writeReplace () вызов вводомMethod создает новый пустой метод. Вызов changeImplementation () используется для замены реализации пустого метода по умолчанию новой единожды; снова загружаем введенное значение поля, а затем просто возвращаем его.
Наконец, поскольку я твердо настроен включить полезный метод toString () практически во все объекты , это также легко сделать в Plastic.
Как только класс был определен, нужно просто вызвать метод newInstance () объекта ClassInstantiator для создания нового экземпляра прокси-класса. За кулисами Plastic создал конструктор для установки значений введенных полей, но еще одна приятная часть Plastic API заключается в том, что вам не нужно управлять этим: ClassInstantiator выполняет свою работу.
Я очень горжусь пластиковыми API в целом; Я думаю, что они находят хороший баланс между упрощением и сжатием обычных операций, но все же предоставляют вам запасной клапан для более мощных (или более эффективных) механизмов, таких как примеры InstructionBuilder выше. Конечно, подход с глубоко вложенными обратными вызовами может поначалу устрашать, но это в основном вопрос синтаксиса, который может быть решен в JDK 8 с добавлением надлежащих замыканий в язык Java.
Я твердо чувствую, что пластик — это инструмент общего назначения, выходящий за рамки инверсии управления и других манипуляций, характерных для гобелена … и пластик был специально разработан для повторного использования вне гобелена. Кажется, что его можно использовать для чего угодно, от реализации простых языков и DSL до предоставления всех видов кода промежуточного программного обеспечения в новых доменах … У меня есть нечеткая идея, включающая JMS и JSON с большим количеством соединений и диспетчеризацией, которые могут быть обработаны с использованием Пластиковые. Я хотел бы услышать идеи других людей!
От http://tapestryjava.blogspot.com/2012/02/plastic-advanced-example.html