Как заменить GWT.create обработчиком аннотаций?

Я хочу создать обработчик аннотаций, который заменяет вызов GWT.create.

С обработчиком аннотаций вам пришлось бы генерировать оба класса, а затем динамически (во время выполнения) выбирать среди них, в зависимости от контекста (вы могли бы создать фабрику, чтобы сделать это, но вам все равно нужно было бы каким-то образом кормить фабрику с текущим контекстом, например текущую локаль).

Источник: https://stackoverflow.com/a/29915793/116472

Я запустил свой процессор аннотаций, он очень хорошо сгенерировал классы. Часть, которую я не знаю, - это часть выбора во время выполнения.

Как я могу сделать этот выбор во время выполнения?


person confile    schedule 29.04.2015    source источник


Ответы (2)


Я предполагаю, что вы рассмотрели сторону генерации кода, и сосредоточимся только на том, как мы можем выбрать правильную реализацию в GWT:

но вам все равно придется как-то кормить фабрику текущим контекстом, например. текущая локаль).

Мы могли бы сделать это во время выполнения, как вы предлагаете, но с недавно добавленной поддержкой System.getProperty вы также можете сделать это во время компиляции.

Первым шагом, конечно же, является создание кода для каждой реализации, к которой вы возможно хотите иметь доступ. Взяв в качестве примера локали, у вас могут быть Foo_en.java, Foo_es.java, Foo_de.java и т. д.

Затем нам нужен согласованный способ получить любую реализацию — возможно, сгенерированный FooFactory, с помощью такого метода:

public static Foo getFoo(String locale) {
  if ("en".equals(locale)) {
    return new Foo_en();
  } else if /*...
  ...*/

  throw new IllegalArgumentException("Locale " + locale + " is not supported");
}

Если вы спросите пользователя во время runtime, какую локаль он хочет, вы можете передать это значение этому фабричному методу, чтобы получить желаемую реализацию. Точно так же, если бы вы могли прочитать это значение во время выполнения из чего-либо, вы могли бы снова получить правильный экземпляр и продолжить работу.


Но что, если вы действительно хотите, чтобы компилятор выбирал за вас? Давайте сохраним этот код, сгенерированный процессором аннотаций, но перенесем фазу выбора локали на компилятор и его перестановки.

Как и в существующем коде GWT, укажите свойство для языкового стандарта и несколько значений. Затем, вместо того, чтобы спрашивать пользователя или решать во время выполнения в своем собственном Java-коде, какую локаль вы хотите, используйте ту же проводку сценария выбора, которую обычно использует GWT (проверяет URL-адрес, файл cookie, метатеги, сам пользовательский агент и т. д.) - вы можете построить свой собственный property-provider для этого, если хотите.

Как и прежде, мы можем использовать getFoo(locale), но теперь мы используем System.getProperty для считывания свойства, которое мы создали в нашем файле .gwt.xml. Это будет статически скомпилировано в правильную константу для каждой перестановки. Но вместо того, чтобы вызывать FooFactory.getFoo(System.getProperty("locale")) каждый раз, когда нам нужен экземпляр, давайте создадим еще один метод в нашей сгенерированной FooFactory:

public static Foo getFoo() {
  return getFoo(System.getProperty("locale"));
}

Теперь мы можем просто вызвать FooFactory.getFoo(), и мы получим правильный класс для нашей текущей перестановки.


Вопрос о кинжале: Thomas, вероятно, гораздо лучше подходит для решения этой проблемы, но нет, Dagger2 не выполняет bind(Foo).to(Bar).in(Scope), как это делает Guice, так как это потребует запуска кода для разрешения привязок, в то время как Dagger работает только с тем, что он может видеть, отражая типы, а не запуск кода. Это означает, что вы в конечном итоге получите множество этих методов @Provides или аннотируете свои фактические типы с подробностями о том, что следует использовать для реализации чего.

FWIW до сих пор я не принял Dagger из-за нескольких причуд, которые потребовали бы от меня переосмысления нескольких битов, и я еще не нашел времени, чтобы сделать это переосмысление:

person Colin Alworth    schedule 29.04.2015
comment
время выполнения все относительно ;-) Из моей точки зрения, System.getProperty() находится во время выполнения, потому что вы реализуете выбор в коде, и тот факт, что код в конечном итоге оптимизируется в константу и одна ветвь кода во время компиляции является деталью реализации (используя мягкие перестановки, она фактически выбирает реализацию во время runtime, System.getProperty() затем заменяется функцией, разрешающей текущее значение времени выполнения) - person Thomas Broyer; 29.04.2015
comment
По этой логике не будет огромным шагом сказать, что генераторы тоже являются средой выполнения... ;) Да, справедливо, хотя, к моей чести, вопрос конкретно задан о среде выполнения - тот факт, что компилятор может сделать среду выполнения решение во время компиляции - это просто дополнительный бонус! - person Colin Alworth; 29.04.2015
comment
Генераторы запускаются в виртуальной машине компилятора точно так же, как процессоры аннотаций запускаются в виртуальной машине javac. Это определенно время компиляции. - person Thomas Broyer; 29.04.2015
comment
Извините, да, очевидно, что процессоры аннотаций запускаются во время компиляции Java, и, очевидно, генератор GWT разрешает свои значения во время компиляции gwt, но аналогично все, что использует компиляцию GWT, может быть разрешено во время компиляции gwt, особенно магические методы, такие как GWT.foo или System.foo. Я не предлагаю кому-либо рассматривать генератор как код времени выполнения, я просто пытаюсь провести различие, что они являются такими же временем компиляции, как и привязка замены на или любой другой постоянный код. Компилятор может это сделать, и программные разрешения ограничивают это, но ограничивают только одну проверку времени выполнения при запуске, а не для каждого вызова. - person Colin Alworth; 29.04.2015
comment
@ColinAlworth Отличный ответ. Пожалуйста, взгляните на мое редактирование. У вас есть какие-нибудь идеи по этому поводу? - person confile; 29.04.2015

Во-первых, вы не обязаны заменять свой GWT.create() вызов. Поскольку генератор GWT выполняет две функции, вы могли бы (это не всегда возможно) заменить часть генерации кода процессором аннотаций, а часть «выборки» — простыми <replace-with> правилами (нацеливание на классы, созданные процессором аннотаций). ).
Также обратите внимание, что GWT.create() на самом деле доступен вне контекста GWT (например, на стороне сервера) с помощью ServerGwtBridge, что позволяет регистрировать экземпляры классов. Обработчик аннотаций может сгенерировать такой экземпляр, или вы можете закодировать его с помощью отражения.

Если вы все-таки хотите заменить свои GWT.create() вызовы, то вам придется использовать фабрику (либо непосредственно в точке вызова, либо обернутую в фасад/прокси). Эта фабрика, возможно, также может быть сгенерирована процессором аннотаций.

Как и в случае любой такой фабрики, код должен быть if…else каскадным или switch…case, чтобы в конечном итоге new соответствующего (сгенерированного) класса; но условия очень специфичны для ваших нужд.

В контексте GWT вы можете использовать GWT.create(UserAgent.class).getCompileTimeValue() для доступа к значению свойства привязки user.agent и, таким образом, заменить <when-property-is name="user.agent" value"…" /> вашего *.gwt.xml на switch…case (обратите внимание, что тогда вам придется самостоятельно обрабатывать резервные варианты; например, ie9 возвращается к ie8, когда нет правила конкретно соответствует ie9). Другими примечательными значениями будут LocaleInfo.getCurrentLocale() в качестве замены свойства привязки locale (но маловероятно, что ваш обработчик аннотаций знает все возможные значения этого свойства, если только вы не передадите их как опции или процессор не прочитает файл модуля GWT, имя которого вы d, вероятно, тоже придется пройти как вариант). LocaleInfo.getCurrentLocale().isRTL() интереснее. Вы также можете использовать такие вещи, как GWT.isClient() или свойства Window.Navigator; и, начиная с GWT 2.8, вы можете использовать System.getProperty() для доступа к вашей конфигурации и свойствам привязки (например, System.getProperty("user.agent", "unknown")). Наконец, конечно, есть JSNI для обнаружения функций, поддерживаемых текущим браузером; но тогда этот код не может быть оптимизирован компилятором GWT, и все сгенерированные классы будут скомпилированы в окончательный JS.

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

person Thomas Broyer    schedule 29.04.2015
comment
Отличный ответ. Пожалуйста, взгляните на мое редактирование. У вас есть какие-нибудь идеи по этому поводу? - person confile; 29.04.2015
comment
Я только что вернул его. Пожалуйста, создайте еще один вопрос (но я видел на форумах Dagger 2, что вы нашли существующий вопрос о той же проблеме) - person Thomas Broyer; 30.04.2015