This example demonstrates how to do compile time checking of an annotated element.
The annotation
The @Setter annotation is a marker can be applied to methods. The annotation will be discarded during compilation not be available afterwards.
package annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.METHOD) public @interface Setter { }
The annotation processor
The SetterProcessor class is used by the compiler to process the annotations. It checks, if the methods annotated with the @Setter annotation are public, non-static methods with a name starting with set and having a uppercase letter as 4th letter. If one of these conditions isn't met, a error is written to the Messager. The compiler writes this to stderr, but other tools could use this information differently. E.g. the NetBeans IDE allows the user specify annotation processors that are used to display error messages in the editor.
package annotation.processor; import annotation.Setter; 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.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; @SupportedAnnotationTypes({"annotation.Setter"}) @SupportedSourceVersion(SourceVersion.RELEASE_8) public class SetterProcessor extends AbstractProcessor { private Messager messager; @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // get elements annotated with the @Setter annotation Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(Setter.class); for (Element element : annotatedElements) { if (element.getKind() == ElementKind.METHOD) { // only handle methods as targets checkMethod((ExecutableElement) element); } } // don't claim annotations to allow other processors to process them return false; } private void checkMethod(ExecutableElement method) { // check for valid name String name = method.getSimpleName().toString(); if (!name.startsWith("set")) { printError(method, "setter name must start with \"set\""); } else if (name.length() == 3) { printError(method, "the method name must contain more than just \"set\""); } else if (Character.isLowerCase(name.charAt(3))) { if (method.getParameters().size() != 1) { printError(method, "character following \"set\" must be upper case"); } } // check, if setter is public if (!method.getModifiers().contains(Modifier.PUBLIC)) { printError(method, "setter must be public"); } // check, if method is static if (method.getModifiers().contains(Modifier.STATIC)) { printError(method, "setter must not be static"); } } private void printError(Element element, String message) { messager.printMessage(Diagnostic.Kind.ERROR, message, element); } @Override public void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); // get messager for printing errors messager = processingEnvironment.getMessager(); } }
Packaging
To be applied by the compiler, the annotation processor needs to be made available to the SPI (see ServiceLoader).
To do this a text file META-INF/services/javax.annotation.processing.Processor needs to be added to the jar file containing the annotation processor and the annotation in addition to the other files. The file needs to include the fully qualified name of the annotation processor, i.e. it should look like this
annotation.processor.SetterProcessor
We'll assume the jar file is called AnnotationProcessor.jar below.
Example annotated class
The following class is example class in the default package with the annotations being applied to the correct elements according to the retention policy. However only the annotation processor only considers the second method a valid annotation target.
import annotation.Setter; public class AnnotationProcessorTest { @Setter private void setValue(String value) {} @Setter public void setString(String value) {} @Setter public static void main(String[] args) {} }
Using the annotation processor with javac
If the annotation processor is discovered using the SPI, it is automatically used to process annotated elements. E.g. compiling the AnnotationProcessorTest class using
javac -cp AnnotationProcessor.jar AnnotationProcessorTest.java
yields the following output
AnnotationProcessorTest.java:6: error: setter must be public private void setValue(String value) {} ^ AnnotationProcessorTest.java:12: error: setter name must start with "set" public static void main(String[] args) {} ^ 2 errors
instead of compiling normally. No .class file is created.
This could be prevented by specifying the -proc:none option for javac. You could also forgo the usual compilation by specifying -proc:only instead.
Learn All in Tamil © Designed & Developed By Tutor Joes | Privacy Policy | Terms & Conditions