Lombok源码

news/2024/11/25 19:41:17/

目录儿

  • jar包结构
  • Main
  • SpiLoadUtil
    • findServices()
    • readServicesFromUrl()
    • META-INF/services/lombok.core.LombokApp
  • ShadowClassLoader
  • Agent
    • lombok.core.AgentLauncher
  • Handler

jar包结构

在这里插入图片描述

Main

这应该是Lombok的入口函数

class Main {private static ShadowClassLoader classLoader;// 这里对 ShadowClassLoader 用了单例模式static synchronized ClassLoader getShadowClassLoader() {if (classLoader == null) {classLoader = new ShadowClassLoader(Main.class.getClassLoader(), "lombok", null, Arrays.<String>asList(), Arrays.asList("lombok.patcher.Symbols"));}return classLoader;}// 先忽略static synchronized void prependClassLoader(ClassLoader loader) {getShadowClassLoader();classLoader.prependParent(loader);}public static void main(String[] args) throws Throwable {ClassLoader cl = getShadowClassLoader();         // 获取 ShadowClassLoader 实例Class<?> mc = cl.loadClass("lombok.core.Main");  // 加载指定类 "lombok.core.Main"try {// 反射调用 lombok.core.Main 的 main 方法mc.getMethod("main", String[].class).invoke(null, new Object[] {args}); } catch (InvocationTargetException e) {throw e.getCause();}}
}

ok,看到这个Main函数的逻辑都是围绕着类加载相关的东西,关键在于这个ShadowClassLoader类加载器[跳转]

加载的这个lombok.core.Main可以在Jar包中找到:

Class<?> mc = cl.loadClass("lombok.core.Main");

在这里插入图片描述
看它源码:

public class Main {// ...public static void main(String[] args) throws IOException {// 首先是设置了当前线程的类加载器为加载这个 Main 类的加载器,// 在 lombok.launch.Main 中知道这个加载器就是 ShadowClassLoader 影子加载器 Thread.currentThread().setContextClassLoader(Main.class.getClassLoader());// 创建 Main 实例,关注它的传入参数int err = new Main(SpiLoadUtil.readAllFromIterator(SpiLoadUtil.findServices(LombokApp.class)), Arrays.asList(args)).go();if (err != 0) {System.exit(err);}}// ...
}

上面创建实例通过SpiLoadUtil.findServices()获取了一些参数,接下来就看看这个工具获取了啥

SpiLoadUtil

上层调用:SpiLoadUtil.findServices(LombokApp.class)

public class SpiLoadUtil {public static <C> Iterable<C> findServices(Class<C> target) throws IOException {// target = LombokApp.class// 第二参数是之前为当前线程设置的 ShadowClassLoader 影子类加载器 return findServices(target, Thread.currentThread().getContextClassLoader()); }}

继续往下找

findServices()

public static <C> Iterable<C> findServices(final Class<C> target, ClassLoader loader) throws IOException {// 没有传入类加载器时,就用系统默认的类加载器if (loader == null) loader = ClassLoader.getSystemClassLoader(); // 获取 META-INF/services/lombok.core.LombokApp 这个资源文件Enumeration<URL> resources = loader.getResources("META-INF/services/" + target.getName());// 创建一个键值对容器,存储资源文件中的元素(类的全限定名)final Set<String> entries = new LinkedHashSet<String>();while (resources.hasMoreElements()) {URL url = resources.nextElement();readServicesFromUrl(entries, url);}final Iterator<String> names = entries.iterator();final ClassLoader fLoader = loader; // 固定类加载器return new Iterable<C> () {         // 遍历加载键值对容器中存储的类@Override public Iterator<C> iterator() {return new Iterator<C>() {@Override public boolean hasNext() {return names.hasNext();}@Override public C next() {try {// 反射创建实例return target.cast(Class.forName(names.next(), true, fLoader).getConstructor().newInstance());} catch (Exception e) {Throwable t = e;if (t instanceof InvocationTargetException) t = t.getCause();if (t instanceof RuntimeException) throw (RuntimeException) t;if (t instanceof Error) throw (Error) t;throw new RuntimeException(t);}}@Override public void remove() {throw new UnsupportedOperationException();}};}};}

接下来深入函数中的readServicesFromUrl(entries, url)和资源文件META-INF/services/lombok.core.LombokApp

readServicesFromUrl()

	private static void readServicesFromUrl(Collection<String> list, URL url) throws IOException {InputStream in = url.openStream();BufferedReader r = null;try {if (in == null) return;r = new BufferedReader(new InputStreamReader(in, "UTF-8"));while (true) {String line = r.readLine();if (line == null) break;int idx = line.indexOf('#'); // 忽略注释if (idx != -1) line = line.substring(0, idx);line = line.trim();if (line.length() == 0) continue;list.add(line); // 存到集合中}} finally {try {if (r != null) r.close();if (in != null) in.close();} catch (Throwable ignore) {}}}

META-INF/services/lombok.core.LombokApp

找到资源文件
在这里插入图片描述
里面的内容是这样的,每一行是一个类的全限定名

# Generated by SpiProcessor
# Mon, 18 Apr 2022 04:23:46 +0200
lombok.bytecode.PoolConstantsApp
lombok.bytecode.PostCompilerApp
lombok.core.Main$LicenseApp
lombok.core.Main$VersionApp
lombok.core.PublicApiCreatorApp
lombok.core.configuration.ConfigurationApp
lombok.core.runtimeDependencies.CreateLombokRuntimeApp
lombok.delombok.DelombokApp
lombok.eclipse.agent.MavenEcjBootstrapApp
lombok.installer.Installer$CommandLineInstallerApp
lombok.installer.Installer$CommandLineUninstallerApp
lombok.installer.Installer$GraphicalInstallerApp

ShadowClassLoader

影子类加载器,继承了普通的类加载器,然后添加了自己的类加载规则

class ShadowClassLoader extends ClassLoader {// 全限定名private static final String SELF_NAME = "lombok/launch/ShadowClassLoader.class";// 类文件后缀private final String sclSuffix;// ...// 构造器ShadowClassLoader(ClassLoader source, // 父·类加载器String sclSuffix,   // 类文件后缀String selfBase,    // 类加载路径List<String> parentExclusion, // 类加载排除名单List<String> highlanders // 这个好像是一个类名单,用来确保类只加载一次) {//...}
}

根据ShadowClassLoader的说明:

The shadow classloader serves to completely hide almost all classes in a given jar file by using a different file ending. The shadow classloader also serves to link in a project as it is being developed (a ‘bin’ dir from an IDE for example).
Classes loaded by the shadowloader use “.SCL.sclSuffix” in addition to “.class”. In other words, most of the class files in a given jar end in this suffix, which serves to hide them from any tool that isn’t aware of the suffix (such as IDEs generating auto-complete dialogs, and javac’s classpath in general). Only shadowloader can actually load these classes.
———————————————————————————————————
意思是之所以要自己搞一个类加载器,是为了对外隐藏Lombok Jar包里面的类,只能通过这个影子类加载器才能加载到这些类,避免被其他工具或插件识别加载。具体的方式是把这些需要隐藏的类文件以.SCL.sclSuffix作为后缀而非常规的.class。因此在Lombok Jar包里面的类文件绝大部分都以.SCL.sclSuffix作为后缀名。(.sclSuffix通常是.lombok

可以从Lombok Jar包里面的类文件印证:
在这里插入图片描述
Main函数中,创建ShadowClassLoader实例的参数如下

classLoader = new ShadowClassLoader(Main.class.getClassLoader(), "lombok", null, Arrays.<String>asList(), Arrays.asList("lombok.patcher.Symbols"));

其中
Main.class.getClassLoader()这个类加载器就是加载Main函数的类加载器,也就是 Spring容器的类加载器
"lombok"指定的是加载的类文件的后缀
null这里没有指定类加载路径
Arrays.<String>asList()指定类加载的排除名单,这里名单为空
Arrays.asList("lombok.patcher.Symbols")这个是把lombok.patcher.Symbols这个类转成数组,而lombok.patcher.Symbols里面是某些类的名单,用来保证名单里面的类只加载一次

Agent

这个是个代理类,实际操作的是lombok.core.AgentLauncher这个类的runAgents()函数

final class Agent {public static void agentmain(String agentArgs, Instrumentation instrumentation) throws Throwable {runLauncher(agentArgs, instrumentation, true);}public static void premain(String agentArgs, Instrumentation instrumentation) throws Throwable {runLauncher(agentArgs, instrumentation, false);}private static void runLauncher(String agentArgs, Instrumentation instrumentation, boolean injected) throws Throwable {// 通过 lombok.launch.Main 拿到影子类加载器实例ClassLoader cl = Main.getShadowClassLoader(); try {Class<?> c = cl.loadClass("lombok.core.AgentLauncher");Method m = c.getDeclaredMethod("runAgents", String.class, Instrumentation.class, boolean.class, Class.class);m.invoke(null, agentArgs, instrumentation, injected, Agent.class);} catch (InvocationTargetException e) {throw e.getCause();}}
}

看看这个 lombok.core.AgentLauncher 是何东西

lombok.core.AgentLauncher

public class AgentLauncher {// 有一个内部接口public interface AgentLaunchable {void runAgent(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Exception;}public static void runAgents(String agentArgs, Instrumentation instrumentation, boolean injected, Class<?> launchingContext) throws Throwable {// 是一个类全限定名集合,实际上只有一个元素:lombok.eclipse.agent.EclipsePatcherfor (AgentInfo info : AGENTS) {try {Class<?> agentClass = Class.forName(info.className());// 反射创建实例AgentLaunchable agent = (AgentLaunchable) agentClass.getConstructor().newInstance();agent.runAgent(agentArgs, instrumentation, injected, launchingContext);} catch (Throwable t) {if (t instanceof InvocationTargetException) t = t.getCause();info.problem(t, instrumentation);}}}
}

lombok.eclipse.agent.EclipsePatcherLombok Jar中的一个类,貌似是用来做补丁的
可以看到在源码中看到lombok.eclipse.agent
在这里插入图片描述
里面的类应该都是用作代理Eclipse工具的类
都知道Lombok是配合开发工具使用的,比如EclipseIDEA,因为它本身应该也要代理这些开发工具的某些参与编译相关的类吧
不过暂时没看到IDEA相关的代理

Handler

javac包(编译相关)中有一个handlers的包,里面全是相关注解的处理类,这应该是Lombok的注解处理核心代码部分
在这里插入图片描述
HandleGetter 为案例分析

public class HandleGetter extends JavacAnnotationHandler<Getter> {// ...
}

其继承了JavacAnnotationHandler,重写了核心函数handler(),然后其他都是自己的处理逻辑
在这里插入图片描述
看处理函数,就是一些注解的判断啊,然后对不同的注解位置做不同的方法注入啊这些

	@Override public void handle(AnnotationValues<Getter> annotation, JCAnnotation ast, JavacNode annotationNode) {handleFlagUsage(annotationNode, ConfigurationKeys.GETTER_FLAG_USAGE, "@Getter");Collection<JavacNode> fields = annotationNode.upFromAnnotationToFields();deleteAnnotationIfNeccessary(annotationNode, Getter.class);deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel");JavacNode node = annotationNode.up();Getter annotationInstance = annotation.getInstance();AccessLevel level = annotationInstance.value(); // 注解值boolean lazy = annotationInstance.lazy();       // 是否懒加载if (lazy) handleFlagUsage(annotationNode, ConfigurationKeys.GETTER_LAZY_FLAG_USAGE, "@Getter(lazy=true)");if (level == AccessLevel.NONE) {if (lazy) annotationNode.addWarning("'lazy' does not work with AccessLevel.NONE.");return;}if (node == null) return;List<JCAnnotation> onMethod = unboxAndRemoveAnnotationParameter(ast, "onMethod", "@Getter(onMethod", annotationNode);switch (node.getKind()) {case FIELD:// 为 Fields 创建 gettercreateGetterForFields(level, fields, annotationNode, true, lazy, onMethod);break;case TYPE:if (lazy) annotationNode.addError("'lazy' is not supported for @Getter on a type.");// 为 Type 创建 gettergenerateGetterForType(node, annotationNode, level, false, onMethod);break;}}

复杂…有空再研究


http://www.ppmy.cn/news/145447.html

相关文章

【PCIE701】基于PCIE总线架构的高性能数据预处理平台

板卡概述 PCIE701是北京青翼科技的一款基于PCI Express总线架构的高性能数据预处理FMC载板&#xff0c;板卡具有1个FMC&#xff08;HPC&#xff09;接口&#xff0c;1个X8 PCIe主机接口&#xff0c;板卡采用Xilinx的高性能Kintex-7系列FPGA作为实时处理器&#xff0c;实现…

知识付费小程序搭建 为有价值的知识买单

以前我们学习写作遇到难题的时候&#xff0c;总喜欢上网搜一下参考资料&#xff0c;但是不知具体从何时起&#xff0c;很多平台内容查看都要钱了。这说明知识付费已经深入到我们的生活中了。再加上疫情爆发这几年&#xff0c;很多教育培训机构都抓住风口&#xff0c;开发了线上…

发那科机器人示教器电缆线_云和发那科机器人维修

广州友仪机电设备有限公司创立于2007年&#xff0c;成长在自动化行业高速发展的高铁上&#xff0c;现在已经成为华南地区具影响力的工控维修品牌&#xff0c;2015年我们再出发&#xff0c;组织公司精英力量设立机器人项目事业部&#xff0c;投入大量经费购入四大家族的主力机型…

台式计算机技术参数响应表,货物报价表及技术参数响应表.doc

货物报价表及技术参数响应表.doc 货物报价表及技术参数响应表 序号货物名称品牌型号单位数量单价(元)总价(元)1黑白激光打印机 联想LJ3700DN17台180030600 2便携式摄像机索尼CX900E1台90009000 3摄像机索尼PXW-X2801台4100041000 4学生电脑惠普商用台式机HP 285 Pro G1 MT140台…

计算机硬件输入出的设备,[计算机硬件及网络]第7章 输入 输出设备.ppt

[计算机硬件及网络]第7章 输入 输出设备 第7章 输入/输出设备 7.1 基础知识 7.2 键盘和鼠标 7.3 扫描仪 7.4 条形码扫描仪 7.5 键盘、鼠标及扫描仪的一般选购方法 7.6 实训案例1&#xff1a;键盘、鼠标的安装 7.7 激光打印机 7.1 基础知识 7.1.1 键盘和鼠标 7.1.2 图形扫描仪和…

图像传感器的市场状况和主要厂商

图像传感器的市场状况和主要厂商 新功能正推动CMOS图像传感器产业变革&#xff0c;未来五年复合年增长率为10.4%&#xff0c;2021年市场规模将达到188亿美元。 行业调研机构Yole认为CMOS图像传感器产业将保持高速增长趋势。智能手机中的摄像头数量增长将消除智能手机出货量增长…

纯干货:CMOS图像传感器产业趋势和主要厂商

新功能正推动CMOS图像传感器产业变革&#xff0c;未来五年复合年增长率为10&#xff0e;4&#xff05;&#xff0c;2021年市场规模将达到188亿美元。 行业调研机构Yole认为CMOS图像传感器产业将保持高速增长趋势。智能手机中的摄像头数量增长将消除智能手机出货量增长缓慢带来的…

(转载)计算机英语名词简释

计算机英语名词简释 一、著名公司及其商标名 Microsoft: 有时缩略为MS&#xff0c;是全球最著名的软件商&#xff0c;美国软件巨头微软公司的名字。Microsoft其实是由两个英语单词组成&#xff1a;Micro意为“微小”&#xff0c;Soft意为“软的”&#xff0c;此处应为“Softwa…