Spring中的100个问题-注解是如何工作的(二)
注解定义了很多影响编译器行为的方式,也使得bean的行为看起来不那么透明。
注解是从编译阶段开始的
Java编译器(java compiler)工作时要经过几个阶段:
- 解析(Parse)阶段[词法、语法解析阶段]
扫描 *.java 源文件, 映射源代码为AST-Nodes(抽象树)
-
生成符号表(symbol table)
-
处理注解(process annotations)
javac默认会处理针对特定语法单位上的注解,比如 class,function,field
- 处理语法树的的属性
这个过程包括名称解析,类型检查和常量打包等
-
数据流(dataflow analysis)分析
-
desugar-重写语法书(AST Rewrite), 翻译一些语法糖
-
最后生成classfile
从java6开始, 在JSR269中定义了如何在编译阶段自定义可插拔(pluggable)的注解API。
在javac的默认输出中可以确认到这个信息
-g 生成所有调试信息
-g:none 不生成任何调试信息
-g:{lines,vars,source} 只生成某些调试信息
-nowarn 不生成任何警告
-verbose 输出有关编译器正在执行的操作的消息
-deprecation 输出使用已过时的 API 的源位置
-classpath <路径> 指定查找用户类文件和注释处理程序的位置
-cp <路径> 指定查找用户类文件和注释处理程序的位置
-sourcepath <路径> 指定查找输入源文件的位置
-bootclasspath <路径> 覆盖引导类文件的位置
-extdirs <目录> 覆盖所安装扩展的位置
-endorseddirs <目录> 覆盖签名的标准路径的位置
-proc:{none,only} 控制是否执行注释处理和/或编译。
-processor <class1>[,<class2>,<class3>...] 要运行的注释处理程序的名称; 绕过默认的搜索进程
-processorpath <路径> 指定查找注释处理程序的位置
-parameters 生成元数据以用于方法参数的反射
-d <目录> 指定放置生成的类文件的位置
-s <目录> 指定放置生成的源文件的位置
-h <目录> 指定放置生成的本机标头文件的位置
-implicit:{none,class} 指定是否为隐式引用文件生成类文件
-encoding <编码> 指定源文件使用的字符编码
-source <发行版> 提供与指定发行版的源兼容性
-target <发行版> 生成特定 VM 版本的类文件
-profile <配置文件> 请确保使用的 API 在指定的配置文件中可用
-version 版本信息
-help 输出标准选项的提要
-A关键字[=值] 传递给注释处理程序的选项
-X 输出非标准选项的提要
-J<标记> 直接将 <标记> 传递给运行时系统
-Werror 出现警告时终止编译
@<文件名> 从文件读取选项和文件名
以下两个输入参数与注解有关。
-processor
-processorpath <路径> 指定查找注释处理程序的位置路径>
如何实现一个注解处理器(annotation procssors)
定义一个继承自AbstractProcessor的子类,同时必须包含两个注解(SupportedAnnotationTypes、SupportedSourceVersion)
必须覆盖的两个接口:
public synchronized void init(ProcessingEnvironment processingEnv)
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
这两个方法在编译阶段被java compiler 调用(仅调用一次)
在maven编译阶段(compiler:compile),显式指定processor可以通过
编译阶段检查代码格式的例子
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 {
}
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();
}
}
public class AnnotationProcessorTest {
@Setter
private void noValue(String value) {}
@Setter
public void setString(String value) {}
@Setter
public static void main(String[] args) {}
}
执行以上代码
javac -Xprint -XprintRounds -XprintProcessorInfo -classpath . -processor SetterProcessor AnnotationProcessorTest.java
demo git:(master) ✗ javac -Xprint -XprintRounds -XprintProcessorInfo -classpath . -processor SetterProcessor AnnotationProcessorTest.java
循环 1:
输入文件: {AnnotationProcessorTest}
注释: [Setter]
最后一个循环: false
OpenJDK8对注解的支持
jdk8u/langtools/src/share/classes/javax
.
├── annotation
│ └── processing
│ ├── AbstractProcessor.java
│ ├── Completion.java
│ ├── Completions.java
│ ├── Filer.java
│ ├── FilerException.java
│ ├── Messager.java
│ ├── ProcessingEnvironment.java
│ ├── Processor.java
│ ├── RoundEnvironment.java
│ ├── SupportedAnnotationTypes.java
│ ├── SupportedOptions.java
│ ├── SupportedSourceVersion.java
│ └── package-info.java
├── lang
│ └── model
│ ├── AnnotatedConstruct.java
│ ├── SourceVersion.java
│ ├── UnknownEntityException.java
│ ├── element
│ │ ├── AnnotationMirror.java
│ │ ├── AnnotationValue.java
│ │ ├── AnnotationValueVisitor.java
│ │ ├── Element.java
│ │ ├── ElementKind.java
│ │ ├── ElementVisitor.java
│ │ ├── ExecutableElement.java
│ │ ├── Modifier.java
│ │ ├── Name.java
│ │ ├── NestingKind.java
│ │ ├── PackageElement.java
│ │ ├── Parameterizable.java
│ │ ├── QualifiedNameable.java
│ │ ├── TypeElement.java
│ │ ├── TypeParameterElement.java
│ │ ├── UnknownAnnotationValueException.java
│ │ ├── UnknownElementException.java
│ │ ├── VariableElement.java
│ │ └── package-info.java
│ ├── overview.html
│ ├── package-info.java
│ ├── type
│ │ ├── ArrayType.java
│ │ ├── DeclaredType.java
│ │ ├── ErrorType.java
│ │ ├── ExecutableType.java
│ │ ├── IntersectionType.java
│ │ ├── MirroredTypeException.java
│ │ ├── MirroredTypesException.java
│ │ ├── NoType.java
│ │ ├── NullType.java
│ │ ├── PrimitiveType.java
│ │ ├── ReferenceType.java
│ │ ├── TypeKind.java
│ │ ├── TypeMirror.java
│ │ ├── TypeVariable.java
│ │ ├── TypeVisitor.java
│ │ ├── UnionType.java
│ │ ├── UnknownTypeException.java
│ │ ├── WildcardType.java
│ │ └── package-info.java
│ └── util
│ ├── AbstractAnnotationValueVisitor6.java
│ ├── AbstractAnnotationValueVisitor7.java
│ ├── AbstractAnnotationValueVisitor8.java
│ ├── AbstractElementVisitor6.java
│ ├── AbstractElementVisitor7.java
│ ├── AbstractElementVisitor8.java
│ ├── AbstractTypeVisitor6.java
│ ├── AbstractTypeVisitor7.java
│ ├── AbstractTypeVisitor8.java
│ ├── ElementFilter.java
│ ├── ElementKindVisitor6.java
│ ├── ElementKindVisitor7.java
│ ├── ElementKindVisitor8.java
│ ├── ElementScanner6.java
│ ├── ElementScanner7.java
│ ├── ElementScanner8.java
│ ├── Elements.java
│ ├── SimpleAnnotationValueVisitor6.java
│ ├── SimpleAnnotationValueVisitor7.java
│ ├── SimpleAnnotationValueVisitor8.java
│ ├── SimpleElementVisitor6.java
│ ├── SimpleElementVisitor7.java
│ ├── SimpleElementVisitor8.java
│ ├── SimpleTypeVisitor6.java
│ ├── SimpleTypeVisitor7.java
│ ├── SimpleTypeVisitor8.java
│ ├── TypeKindVisitor6.java
│ ├── TypeKindVisitor7.java
│ ├── TypeKindVisitor8.java
│ ├── Types.java
│ └── package-info.java
└── tools
├── Diagnostic.java
├── DiagnosticCollector.java
├── DiagnosticListener.java
├── DocumentationTool.java
├── FileObject.java
├── ForwardingFileObject.java
├── ForwardingJavaFileManager.java
├── ForwardingJavaFileObject.java
├── JavaCompiler.java
├── JavaFileManager.java
├── JavaFileObject.java
├── OptionChecker.java
├── SimpleJavaFileObject.java
├── StandardJavaFileManager.java
├── StandardLocation.java
├── Tool.java
├── ToolProvider.java
├── overview.html
└── package-info.java
篇幅原因,下一篇分析spring中的注解。