Custom Annotation Preprocessor — создание на базе Android-приложения и конфигурация в IntelliJ IDEA

от автора

Всем привет!

Недавно передо мной встала задача написания своих кастомных аннотации и их обработки во время компиляции. Первый вопрос, который я себе задала: с чего начать? После анализа я решила поделиться с вами ответом на этот вопрос.
Думаю, рассказывать, что такое аннотации в java и с чем их едят, не имеет смысла, так как каждому юному программисту это знакомо ( а кому не знакомо, может прочесть самостоятельно). К тому же на хабре есть интересная ознакомительная статья об этом явлении.
Но сегодня я хочу поговорить именно о кастомных аннотациях в Android-приложении, которые обрабатываются в процессе компиляции проекта вашим собственным обработчиком и о автогенерации классов на их основе. А так же, по ходу дела, расскажу вам, как быстро все настроить в IDEA (сама я пользуюсь версией 12.1, возможно в других есть отличия).

Для реализации нам понадобится 2 проекта: в первом опишем наши кастомные аннотации и их обработчик, из него сгенерируем jar файл, который подключим ко второму тестовому проекту, который и будет использовать наши аннотации.

Шаг 1

Создаем новый library проект, в котором описываем свою аннотацию. Класс с аннотацией выглядит примерно так:

@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface CustomAnnotation {      String className();     String value() default "Hello";     int type() default 0; } 

@Retention говорит о том, что наша аннотация будет присутствовать только в исходном коде и отброшена компилятором (а до этого момента мы её обработаем).

Шаг 2

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

@SupportedAnnotationTypes({"*"}) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class CustomProcessor extends AbstractProcessor {  } 

Далее переопределяем метод process, в котором прописываем логику генерации нового класса. Мой метод выглядит так:

@Override     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {          for (Element e : roundEnv.getElementsAnnotatedWith(CustomAnnotation.class)) {             CustomAnnotation ca = e.getAnnotation(CustomAnnotation.class);             String name = e.getSimpleName().toString();             char[] c = name.toCharArray();             c[0] = Character.toUpperCase(c[0]);             name = new String(name);             TypeElement clazz = (TypeElement) e.getEnclosingElement();             try {                 JavaFileObject f = processingEnv.getFiler().                         createSourceFile(clazz.getQualifiedName() + "Autogenerate");                 processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,                         "Creating " + f.toUri());                 Writer w = f.openWriter();                 try {                     String pack = clazz.getQualifiedName().toString();                     PrintWriter pw = new PrintWriter(w);                     pw.println("package "                             + pack.substring(0, pack.lastIndexOf('.')) + ";");                     pw.println("\npublic class "                             + clazz.getSimpleName() + "Autogenerate {");                      TypeMirror type = e.asType();                      pw.println("\n    public " + ca.className() + " result = \"" + ca.value() + "\";");                      pw.println("    public int type = " + ca.type() + ";");                       pw.println("\n    protected " + clazz.getSimpleName()                             + "Autogenerate() {}");                     pw.println("\n    /** Handle something. */");                     pw.println("    protected final void handle" + name                             + "(" + ca.className() + " value" + ") {");                     pw.println("\n//" + e);                     pw.println("//" + ca);                     pw.println("\n        System.out.println(value);");                     pw.println("    }");                     pw.println("}");                     pw.flush();                 } finally {                     w.close();                 }             } catch (IOException x) {                 processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,                         x.toString());             }         }         return true;     } 

На этом этапе можно включить фантазию и писать, что душе угодно (ну или что требуется =). После того, как вы закончили с обработчиком аннотаций и описали все свои кастомные аннотации, из этого проекта нужно сгенерировать jar файл. В Idea 12 это делается достаточно просто: Project Settings -> Artifacts -> Add -> Jar -> From modules… Далее делаем Build -> Rebuild Project и находит наш сгенерированный файл jar в Output директории проекта.

Шаг 3

Создаем тестовый проект, в котором и будем использовать наши кастомные аннотации. К проекту подключаем сгенерированный на прошлом шаге jar файл и радуемся, что наши аннотации теперь нам доступны. В любом классе прописываем нашу аннотацию, например так:

public class MyActivity extends Activity {      public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.main);     }      @CustomAnnotation(className = "String", type = 1)     public void annotatedMethod(String value) {     } } 

Помните, что мы указали нашей аннотации @Target(ElementType.METHOD), а это значит что мы можем прописать ее только перед методом.

Шаг 4

Теперь скажем Idea использовать наш обработчик аннотаций и магия начнет работать! Для этого идём в Settings в раздел Compiler — > Annotation Processors. Ставим галочку Enable annotation processing, в поле Processor path указываем путь к нашему сгенерированному jar файлу. Так же в окне Processor FQ name вводим полное название класса, который отвечает за обработку. В нашем случает это CustomProcessor. Заполненное окно должно выглядеть примерно так.

Шаг 5

Делаем Build -> Rebuild project и наслаждаемся результатами. В дереве проекта должна появиться папка generated, в которой будут лежать новые файлы.
Вот что получилось у меня:

public class MyActivityAutogenerate {      public String result = "Hello";     public int type = 1;      protected MyActivityAutogenerate() {}      /** Handle something. */     protected final void handleannotatedMethod(String value) {  //annotatedMethod(java.lang.String) //@com.example.AnnotationsProcessor.CustomAnnotation(type=1, value=Hello, className=String)          System.out.println(value);     } } 

Happy codding!

ссылка на оригинал статьи http://habrahabr.ru/post/200354/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *