Статьи

Продвинутый пример пластика

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