Как известно, Java имеет уже довольно таки развитые механизмы мета-программирования посредством использования аннотаций. Самым интересным в этих механизмах является возможность создания так называемого процессора аннотаций, который будет вызываться компилятором автоматически. Однако в процессе разработки таких процессоров аннотаций возникает одна не очень тривиальная проблема - как осуществить тестирование и отладку самого процессора аннотаций?
Гуглинг выдает некоторые инструкции по этому поводу, но они предписывают использовать довольно сложные средства разработки плагинов Eclipse, создавать плагин, в нем определять точку расширения и осуществлять отладку этого плагина. На мой взгляд эту проблему можно решить значительно проще.
С помощью вот такой очень несложной функции становится очень легко тестировать, и (что самое главное!) дебажить процессоры аннотаций в Java:
В Project Explorer выбрать мышкой проект, нажать правую кнопку мышки, в появившемся контекстном меню нажать на пункт Build Path а в появившемся при этом подменю - пункт Configure Build Path ... . В открывшемся окне выбрать вкладку Libraries и нажать кнопку (справа список кнопок) Add Variable ... . В новом окне нажать сначала кнопку Configure Variables ... (она снизу под списком переменных) и в новом окне создать новую переменную JDK_HOME в которой указать путь к вашему JDK. Нажать OK, и вернувшись в предыдущее окно, выбрать в списке вновь созданную переменную JDK_HOME и нажать справа сверху от списка переменных кнопку Extend. В появившемся окне выбрать в дереве каталог lib, раскрыть его, и выбрать файл tools.jar. Дальше нажимать OK в каждом из диалогов. Всё, библиотека tools.jar подключена к вашему проекту.
Вот пример самого простого annotation processor:
Затем полученный процессор аннотаций, упакованный в jar, вместе с файлом
Подробнее по ссылкам:
Гуглинг выдает некоторые инструкции по этому поводу, но они предписывают использовать довольно сложные средства разработки плагинов Eclipse, создавать плагин, в нем определять точку расширения и осуществлять отладку этого плагина. На мой взгляд эту проблему можно решить значительно проще.
С помощью вот такой очень несложной функции становится очень легко тестировать, и (что самое главное!) дебажить процессоры аннотаций в Java:
package ap.util; import java.io.File; import javax.tools.JavaCompiler; import javax.tools.ToolProvider; public class CompilerUtil { public static boolean compile(File... files) { final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); return compiler.getTask(null, null, null, null, null, compiler.getStandardFileManager(null, null, null) .getJavaFileObjects(files)).call(); } }Чтобы это работало необходимо чтобы в classpath присутствовал tools.jar из JDK. В Eclipse этого присутствия можно добиться следующим образом:
В Project Explorer выбрать мышкой проект, нажать правую кнопку мышки, в появившемся контекстном меню нажать на пункт Build Path а в появившемся при этом подменю - пункт Configure Build Path ... . В открывшемся окне выбрать вкладку Libraries и нажать кнопку (справа список кнопок) Add Variable ... . В новом окне нажать сначала кнопку Configure Variables ... (она снизу под списком переменных) и в новом окне создать новую переменную JDK_HOME в которой указать путь к вашему JDK. Нажать OK, и вернувшись в предыдущее окно, выбрать в списке вновь созданную переменную JDK_HOME и нажать справа сверху от списка переменных кнопку Extend. В появившемся окне выбрать в дереве каталог lib, раскрыть его, и выбрать файл tools.jar. Дальше нажимать OK в каждом из диалогов. Всё, библиотека tools.jar подключена к вашему проекту.
Вот пример самого простого annotation processor:
package ap.sample; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; @SupportedAnnotationTypes(value = { "*" }) @SupportedSourceVersion(SourceVersion.RELEASE_6) public class APSample extends AbstractProcessor { private Messager messager; @Override public synchronized void init( ProcessingEnvironment processingEnv) { messager = processingEnv.getMessager(); } @Override public boolean process( final Set<? extends TypeElement> annotations, final RoundEnvironment env) { for (final TypeElement annotation : annotations) { for (final Element annotated : env.getElementsAnnotatedWith(annotation)) { messager.printMessage(Kind.WARNING, annotation.getSimpleName(), annotated); } } return true; } }Чтобы он автоматически вызывался при компиляции, нужно в каталоге
META-INF/services
создать файл с названием javax.annotation.processing.Processor
где просто указать полное имя класса, в данном случае:
ap.sample.APSampleТеперь можно создать например такую тестовую аннотацию:
package ap.sample; public @interface ApSampleAnnotation { }и аннотировать ней тестовый класс:
package ap.sample; @ApSampleAnnotation public class ApSampleClass { }Теперь можно создать такой unit-test:
package ap.sample; import static org.junit.Assert.assertTrue; import static ap.util.CompilerUtil.compile; public class ApSampleTest { @Test public void testAp() { assertTrue(compile(new File( "test/ap/sample/ApSampleClass.java"))); } }, поставить в коде процессора аннотаций, то есть в классе APSample, где нибудь точку прерывания отладчика (debug breakpoint), запустить отладку теста ApSampleTest (Debug as ... / JUnit test) и посмотреть как отладчик остановится там где мы поставили ему точку прерывания. Что интересно, стектрейс при этом будет таким:
APSample.process(Set<TypeElement>, RoundEnvironment) line: 31 JavacProcessingEnvironment.callProcessor(Processor, Set<TypeElement>, RoundEnvironment) line: 627 JavacProcessingEnvironment.discoverAndRunProcs(Context, Set<TypeElement>, List<ClassSymbol>, List<PackageSymbol>) line: 556 JavacProcessingEnvironment.doProcessing(Context, List<JCCompilationUnit>, List<ClassSymbol>, Iterable<PackageSymbol>) line: 701 JavaCompiler.processAnnotations(List<JCCompilationUnit>, List<String>) line: 987 JavaCompiler.compile(List<JavaFileObject>, List<String>,Iterable<Processor>) line: 727 Main.compile(String[], Context, List<JavaFileObject>, Iterable<Processor>) line: 353 JavacTaskImpl.call() line: 115 CompilerUtil.compile(File...) line: 14 ApSampleTest.testAp() line: 16 ...А так же мы получим от компилятора сообщение о том что процесс компиляции был прерван изза warning'a который сгенерировал наш процессор, что и докажет нам что всё честно и мы находимся прямо посреди процесса компиляции java-класса.
Затем полученный процессор аннотаций, упакованный в jar, вместе с файлом
META-INF/services/javax.annotation.processing.Processor
внутри (в котором он прозрачно подключается к процессу компиляции), можно очень удобно использовать в Eclipse через его механизмы поддержки таких процессоров. Подключается процессор аннотаций к проекту в диалоге Properties проекта в пункте Java Compiler/Annotation Processing/Factory Path, там нужно просто нажать Add Jar (или Add Variable, это по сути тоже самое, разница только в том как будет определен путь к файлу) и там выбрать jar с процессором. После этого проект будет автоматически использовать процессор аннотаций прямо в среде разработки. Подробнее по ссылкам:
- http://jcp.org/en/jsr/detail?id=269
- http://docs.oracle.com/javase/6/docs/api/javax/annotation/processing/package-summary.html
- http://docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html
- http://docs.oracle.com/javase/tutorial/java/javaOO/annotations.html
- http://www.eclipse.org/jdt/apt/introToAPT.html
Комментариев нет:
Отправить комментарий