文章估计一时半会写不完。lombok的原理还是挺有意思的
先说说注解吧。来自官方的文档。(地址:https://docs.oracle.com/javase/tutorial/java/annotations/)
这里有三句话:
①:Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings
注解可以用来被编译器检测错误或者阻止错误。
②:Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth
软件工具可以处理注解信息生成代码 xml文件
③:Runtime processing — Some annotations are available to be examined at runtime
一些注解可以在运行时检测。
这里的③,其实就是我们在spring中经常见到了。其中①就像我们平时用的@override是用来给javac看的。
再看一下javac的参数:
C:\Program Files\Java\jdk1.7.0_75\bin>javac
用法: javac <options> <source files>
其中, 可能的选项包括:
-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 <路径> 指定查找注释处理程序的位置
-d <目录> 指定放置生成的类文件的位置
-s <目录> 指定放置生成的源文件的位置
-implicit:{none,class} 指定是否为隐式引用文件生成类文件
-encoding <编码> 指定源文件使用的字符编码
-source <发行版> 提供与指定发行版的源兼容性
-target <发行版> 生成特定 VM 版本的类文件
-version 版本信息
-help 输出标准选项的提要
-A关键字[=值] 传递给注释处理程序的选项
-X 输出非标准选项的提要
-J<标记> 直接将 <标记> 传递给运行时系统
-Werror 出现警告时终止编译
@<文件名> 从文件读取选项和文件名
C:\Program Files\Java\jdk1.7.0_75\bin>
其中
-classpath <路径> 指定查找用户类文件和注释处理程序的位置
-cp <路径> 指定查找用户类文件和注释处理程序的位置
足可以看出第①个的作用。
===========
先看一个注解改为javac行为的例子。
代码来自网络.
贴代码:
import faicm.PrintMe;
public class A {
public static void main(String[] args) {
aa();
}
@PrintMe
private static void aa() {
}
}
package faicm;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;
@SupportedAnnotationTypes({"faicm.PrintMe"})
public class MyProcessor extends AbstractProcessor {
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
Messager messager = processingEnv.getMessager();
for (TypeElement te : annotations) {
for (Element e : env.getElementsAnnotatedWith(te)) {
messager.printMessage(Diagnostic.Kind.NOTE, "zouqi "
+ "Printing: " + e.toString());
}
}
return true;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
继续
package faicm;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE)
public @interface PrintMe {
}
继续
javax.annotation.processing.Processor文件里面就一句话
faicm.MyProcessor
MANIFEST.MF文件 就一句话 Manifest-Version: 1.0
打包成jar后。如下:
C:\Program Files\Java\jdk1.7.0_75\bin>javac -cp ann.jar A.java
注: zouqi Printing: aa()
C:\Program Files\Java\jdk1.7.0_75\bin>
第二个知识点:
在jar里面有这么一个接口和类:
package faicm;
public interface Animal {
public void say();
}
==
package faicm;
public class Dog implements Animal {
public void say() {
System.out.println("dog");
}
}
=
package faicm;
public class Cat implements Animal {
public void say() {
System.out.println("cat");
}
}
在项目下有一个文件夹META-INF/services 下有faicm.Animal文件
里面就两句话:
faicm.Dog
faicm.Cat
把项目打jar后,在另一个项目中
public class Main {
public static void main(String[] args) {
ServiceLoader<Animal> loder = ServiceLoader.load(Animal.class);
for (Animal animal : loder) {
System.out.println(animal.getClass());
animal.say();
}
}
}
interface Animal {
public void say();
就能看到输出了。
在lombok.jar中可以看到这个文件:
javax.annotation.processing.Processor
里面的内容是
lombok.launch.AnnotationProcessorHider$AnnotationProcessor
其中Processor在api中可以查到。AnnotationProcessor这个类就是lombok工作的类。
其中就是这句作为入口
return instance.process(annotations, roundEnv);
@Override boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return processor.process(annotations, roundEnv);
}
Processor.java类的
public boolean process(Set <? extends TypeElement> annotations, RoundEnvironment roundEnv)
这个方法开始和javac打交道。
javac中是怎么样的呢。未完待续。。。
继续写:
一:下载openjdk源码openjdk-7u40-fcs-src-b43-26_aug_2013.zip 解压后把openjdk\langtools\src\share\classes 这个文件夹里面的所有文件放到eclipse项目src下,如果报错。调整一下jdk就行了。
二:下载lombok的jar文件lombok-1.16.4.jar,打进项目。
三:创建类放在d盘:
import lombok.Setter;
public class Person {
@Setter
private String name;
@Setter
private int age;
}
然后在项目中找到类com.sun.tools.javac下的Main执行主方法,加参数d:/Person.java,运行后生成class文件,反编译后看到:
public class Person
{
private String name;
private int age;
public void setName(String paramString)
{
this.name = paramString;
}
public void setAge(int paramInt) { this.age = paramInt; }
}
好了,环境就是这样搞ok。
在lombok.launch下的AnnotationProcessorHider中的@Override public boolean process里面debug就进去了。
在openjdk里面:
JavacProcessingEnvironment 中有一个内部类是ServiceIterator 。
ServiceIterator 构造方法
loaderClass = Class.forName(“java.util.ServiceLoader”);
loadMethodName = “load”;
jusl = true;
这个就是对应了上面的知识点2:如下图:
未完待续。
如图,会经过这个方法
public JavaCompiler processAnnotations(List<JCCompilationUnit> roots,
List<String> classnames) {
if (shouldStop(CompileState.PROCESS)) {
// Errors were encountered.
// Unless all the errors are resolve errors, the errors were parse errors
// or other errors during enter which cannot be fixed by running
// any annotation processors.
if (unrecoverableError()) {
log.reportDeferredDiagnostics();
return this;
}
}
第一个参数是一个AST,
JavaCompiler c = procEnvImpl.doProcessing(context, roots, classSymbols, pckSymbols);
boolean errorStatus;
boolean moreToDo;
do {
// Run processors for round n
round.run(false, false);
discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles);
if (matchedNames.size() > 0 || ps.contributed) {
boolean processingResult = callProcessor(ps.processor, typeElements, renv);
ps.contributed = true;
ps.removeSupportedOptions(unmatchedProcessorOptions);
if (printProcessorInfo || verbose) {
log.printNoteLines("x.print.processor.info",
ps.processor.getClass().getName(),
matchedNames.toString(),
processingResult);
}
if (processingResult) {
unmatchedAnnotations.keySet().removeAll(matchedNames);
}
}
继续走
lombok的jar在eclipse进不去,所以可以断点看不到代码。
未完待续。