1. Некоторые основные понятия
Прежде чем мы начнем, нам нужно объявить важную вещь: мы обсуждаем не аннотации, которые обрабатываются с помощью механизмов размышлений во время выполнения, а обсуждаем аннотации, которые обрабатываются во время компиляции.
В чем разница между аннотациями времени компиляции и аннотациями времени выполнения? На самом деле, это не большое, в основном из -за проблем с производительностью. Аннотации времени выполнения в основном полагаются полностью на размышления, и эффективность отражения медленнее, чем у местных, поэтому на машинах будет некоторое отставание с меньшей памятью и плохим процессором. Тем не менее, у аннотации вообще не будет этой проблемы, потому что в нашем процессе компиляции (Java-> класса) некоторые марки аннотации используются для динамического генерации некоторых классов или файлов, поэтому это не имеет никакого отношения к нашей операции APK, поэтому, естественно, нет проблемы с производительностью. Поэтому, если более известный проект с открытым исходным кодом использует функцию аннотации, он обычно использует аннотацию времени компиляции.
Аннотационный процессор - это инструмент, который поставляется с Javac, который используется для сканирования и обработки информации аннотации в течение периода компиляции. Вы можете зарегистрировать свой собственный процессор аннотации для определенных аннотаций. Здесь я предполагаю, что вы уже понимаете, что такое аннотации и как их настроить. Если вы еще не поняли аннотации, вы можете проверить официальную документацию. Процессоры аннотации уже существовали в Java 5, но не было API до Java 6 (выпущена в декабре 2006 года). Потребовалось некоторое время, прежде чем пользователи Java осознали силу процессора аннотации. Таким образом, это стало популярным только в последние годы.
Процессор с определенной аннотацией принимает исходный код Java (или скомпилированный байт -код) в качестве входного, а затем генерирует некоторые файлы (обычно. Что это значит? Вы можете генерировать код Java! Эти коды Java находятся в сгенерированном файле .java. Поэтому вы не можете изменить существующий класс Java, например, добавление метода. Эти сгенерированные файлы Java будут составлены Javac, как и другой, написанный вручную исходный код Java.
Обработка аннотаций выполняется на стадии компиляции. Его принцип - прочитать исходный код Java, проанализировать аннотации, а затем генерировать новый код Java. Недавно сгенерированный код Java наконец -то составлен в Java Bytecode. Процессор аннотации не может изменить класс для чтения Java, такой как добавление или удаление методов Java.
2. Аннотация
Давайте посмотрим на API процессора. Все процессоры наследуют абстрактный процессор, как показано ниже:
Пакет com.example; import java.util.linkedhashset; import java.util.set; import javax.annotation.processing.abstractprocessor; import javax.annotation.processing.processingEnviron; импорт javax.annotation.processing.roundenvirment; javax.annotation.processing.SupportedAnnotationTypes;import javax.annotation.processing.SupportedSourceVersion;import javax.lang.model.SourceVersion;import javax.lang.model.element.TypeElement;public class MyProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends Typelement> объявления, roundenvironment env) {return false; } @Override public set <String> getSupportEnnotationTypes () {set <string> annotataions = new LinkedHashset <string> (); annotataions.add ("com.example.myannotation"); вернуть аннотатаоны; } @Override public Sourceversion getSupportedSourceVersion () {return SourceVersion.latestSupported (); } @Override public synchronized void init (ProcessingEnvironment ProcessingEnv) {super.init (ProcessingEnv); }}init (ProcessingEnvironment ProcessingEnv): все классы процессора аннотации должны иметь конструктор без параметра. Тем не менее, существует специальный метод init (), который называется инструментом обработки аннотаций, принимая обработку среды в качестве параметра. Обработка среда обеспечивает некоторые практические элементы классов инструментов, типы и заявки. Мы будем использовать их позже.
process(Set<? extends TypeElement> annoations, RoundEnvironment env) : это похоже на метод Main () каждого процессора. Вы можете кодировать и реализовать сканирование, обработку аннотаций и генерировать файлы Java в этом методе. Используя параметр Roundenvironment, вы можете запросить элементы, аннотированные с определенной аннотацией (исходный текст: вы можете запросить элементы, аннотированные с определенной аннотацией). Мы увидим детали позже.
getSupportedAnnotationTypes(): В этом методе необходимо указать, какие аннотации должны быть зарегистрированы процессором аннотации. Обратите внимание, что его возвратное значение - это коллекция строк, которая содержит полное имя типа аннотации, который хочет обработать ваш процессор аннотации. Другими словами, вы определяете здесь, с какими аннотациями будут обращаться ваш процессор аннотации.
getSupportedSourceVersion() : используется для указания используемой вами версии Java. Обычно вам следует возвращать SourceVersion.latestSupported() . Однако, если у вас достаточно причин придерживаться Java 6, вы также можете вернуть SourceVersion.RELEASE_6 . Я рекомендую использовать SourceVersion.latestSupported() . В Java 7 вы также можете использовать аннотации, чтобы заменить переписывание getSupportedAnnotationTypes() и getSupportedSourceVersion() , как показано ниже:
@SupportedSourceVersion(value=SourceVersion.RELEASE_7)@SupportedAnnotationTypes({ // Set of full qulified annotation type names "com.example.MyAnnotation", "com.example.AnotherAnnotation" })public class MyProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> announcements, Roundenvironment env) {вернуть false; } @Override public synchronized void init (ProcessingEnvironment ProcessingEnv) {super.init (ProcessingEnv); }} Из -за проблем совместимости, особенно для Android, я рекомендую переписать getSupportedAnnotationTypes() и getSupportedSourceVersion() вместо использования @SupportedAnnotationTypes и @SupportedSourceVersion .
Следующее, что вы должны знать, это: процессор аннотации работает в своем собственном JVM. Да, вы правильно прочитали. Javac запускает полную виртуальную машину Java для запуска процессора аннотации. что это значит? Вы можете использовать все, что используете в обычной программе Java. Используя гуаву! Вы можете использовать инструменты впрыскивания зависимостей, такие как кинжал или любая другая библиотека классов, которую вы хотите использовать. Но не забывайте, что даже если это просто крошечный процессор, вы должны обратить внимание на использование эффективных алгоритмов и моделей дизайна, как вы делаете при разработке других программ Java.
3. Зарегистрируйте процессор
Вы можете спросить: «Как зарегистрировать мой процессор аннотации в Javac?». Вы должны предоставить файл .jar. Как и другие файлы .jar, вы упаковываете уже скомпилированный процессор аннотации в этот файл. И в вашем файле .jar вы должны упаковать специальный файл javax.annotation.processing.processor в каталог мета-инф/услуг. Итак, ваша структура каталогов файлов .JAR выглядит так:
Myprocess.jar -com -example -myprocess.class -meta -inf -services -javax.annotation.processing.processor
Содержимое файла javax.annotation.processing.processor является списком, и каждая строка является полным именем процессора аннотации. Например:
com.example.myprocess
com.example.anotherprocess
4. Пример: заводская модель
Проблема, которую мы хотим решить: мы хотим внедрить магазин пиццы, который предоставляет клиентам две пиццы (Margerita и Calzone), а также десерт Tiramisu (Tiramisu).
Общедоступный интерфейс еда {public float getPrice ();} открытый класс Margheritapizza реализует еду {@Override public float getPrice () {return 6.0f; }} открытый класс calzonepizza реализует еду {@override public float getPrice () {return 8.5f; }} открытый класс Tiramisu реализует еду {@override public float getPrice () {return 4.5f; }} public Class PizzaStore {public nial order (string name) {if (null == nailname) {throw new allogalargumentException («Имя еды null!»); } if ("margherita" .equals (yalname)) {return new margheritapizza (); } if ("calzone" .equals (еде)) {return new calzonepizza (); } if ("tiramisu" .equals (yalname)) {return new tiramisu (); } бросить новое allosalargumentException ("Неизвестно еда" + еда " +" '"); } частная статическая строка readconsole () {scanner scanner = new Scanner (System.in); String Meal = scanner.nextline (); scanner.close (); вернуть еду; } public static void main (string [] args) {System.out.println ("Добро пожаловать в Pizza Store"); Pizzastore Pizzastore = New Pizzastore (); Еда еда = pizzastore.order (readconsole ()); System.out.println ("Билл: $" + еда.getprice ()); }}Как вы можете видеть, в методе Order () у нас есть много заявлений о суждениях. И, если мы добавим новую пиццу, мы должны добавить новое условное суждение. Но подождите, используя процессор аннотации и заводский режим, мы можем заставить процессор аннотации генерировать их, если операторы. Таким образом, код, который мы хотим, выглядит так:
Public Class Pizzastore {Private FieFactory Factory = new FieFactory (); Общественный заказ на еду (string name) {return factory.create (еда имя); } частная статическая строка readconsole () {scanner scanner = new Scanner (System.in); String Meal = scanner.nextline (); scanner.close (); вернуть еду; } public static void main (string [] args) {System.out.println ("Добро пожаловать в Pizza Store"); Pizzastore Pizzastore = New Pizzastore (); Еда еда = pizzastore.order (readconsole ()); System.out.println ("Билл: $" + еда.getprice ()); }} public Class foodFactory {public naud Create (String id) {if (id == null) {бросить новый allosalargumentException ("ID is null!"); } if ("calzone" .equals (id)) {return new calzonepizza (); } if ("tiramisu" .equals (id)) {return new tiramisu (); } if ("margherita" .equals (id)) {return new margheritapizza (); } бросить новый allosalargumentException ("Неизвестный id =" + id); }}5. @Factory Annotation
Можете ли вы догадаться, что мы планируем использовать процессор аннотации для создания класса Fiefactory. В более общем плане мы хотим предоставить аннотацию и процессор для создания заводских классов.
Давайте посмотрим на аннотацию @Factory:
@Target (elementType.type) @retention (armentpolicy.class) public @Interface factory { / ** * Имя завод * / class <?> Type (); / ** * Идентификатор для определения того, какой элемент должен быть создан */ string id ();}Идея заключается в следующем: мы аннотируем эти классы питания, используем тип (), чтобы указать, к какой фабрике принадлежит этот класс, и используем id (), чтобы указать конкретный тип этого класса. Давайте применим аннотацию @Factory к этим классам:
@Factory (type = margheritapizza.class, id = "margherita") открытый класс Margeeritapizza реализует еду {@override public float getPrice () {return 6.0f; }} @Factory (type = calzonepizza.class, id = "calzone") открытый класс calzonepizza реализует еду {@override public float getPrice () {return 8.5f; }} @Factory (type = tiramisu.class, id = "tiramisu") открытый класс Tiramisu реализует еду {@override public float getPrice () {return 4.5f; }}Вы можете спросить, можем ли мы просто применить аннотацию @Factory к интерфейсу еды? Ответ - нет, потому что аннотация не может быть унаследована. То есть, если в классе X есть аннотации, класс Y расширяет X, то класс Y не будет наследовать аннотации в классе X. Прежде чем мы напишите процессор, нам нужно прояснить несколько правил:
Аннотация процессор:
Public Class FactoryProcessor расширяет AbstractProcessor {Private Types Typeutils; частные элементы elementutils; частный регистратор; Частный Messager Messager; Частная карта <строка, factorygroupedclasses> factoryclasses = new LinkedHashmap <String, FactoryGroupedClasses> (); @Override public synchronized void init (ProcessingEnvironment ProcessingEnv) {super.init (ProcessingEnv); typeutils = processingenv.gettypeutils (); elementutils = processingEnv.getElementutils (); fileer = processingenv.getfiler (); messager = processingenv.getmessager (); } @Override Public Boolean Process (set <? Extends TypeLement> arg0, roundenvironment arg1) {... вернуть false; } @Override public set <String> getSupportEnnotationTypes () {set <string> annotataions = new LinkedHashset <string> (); annotataions.add (factory.class.getCanonicalName ()); вернуть аннотатаоны; } @Override public Sourceversion getSupportedSourceVersion () {return SourceVersion.latestSupported (); }} В методе getSupportedAnnotationTypes() мы указываем, что аннотация @Factory будет обработана этим процессором.
Суммировать
Вышеуказанное - все содержание этой статьи. Я надеюсь, что содержание этой статьи имеет определенную справочную ценность для каждого обучения или работы. Если у вас есть какие -либо вопросы, вы можете оставить сообщение для общения. Спасибо за поддержку Wulin.com.