Как известно, 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