When the lombok jar file is opened in Intellij, all files other than the annotations end with .SCL.lombok (e.g. HandleAccessors.SCL.lombok
). I was just wondering what the reason for this was and how it's handled.
CodePudding user response:
The reason for it
Lombok has a public API - the stuff you're supposed to interact with. That'd be, for example, the @lombok.Getter
annotation. Those are just class files in that jar, the aim is simply: add that jar to your classpath and your IDE autocomplete dialogs and the like will automatically start suggesting these, as per design.
But, lombok also has lots of classes that just 'make it tick', these aren't meant for public consumption. Things like lombok.eclipse.HandleGetter
, which is the implementation for handling the @Getter
annotation inside the eclipse agent. There is no point or purpose to referring to this class anywhere, in any project - it's an internal lombok thing. If we just stuck that jar file into the jar, and you typed Handle
and hit your IDE's autocomplete shortcut key, you'd still get the suggestion.
Similarly, we ship a few dependencies straight into lombok.jar - it's a 'shaded jar' (a jar with all deps included), though we don't have many, keeping lombok.jar
a nice small size. Still, ASM (a bytecode manipulation library) is in it, and that is fairly popular.
The standard shading solution offered by most shading tools is to prefix something to the name. ASM's org.objectweb.asm.AnnotationVisitor
class would become org.projectlombok.shading.org.objectweb.asm.AnnotationVisitor
. Point is, your IDE doesn't know that, and if you ALSO use asm in your project (where you also use lombok), and you want AnnotationVisitor thus you type AnnV and hit cmd space or whatnot, your IDE suggests both. That's ugly and we'd like to avoid this.
Hence, we built our own shader, and it works by not having class files in the first place. This way, IDEs and any other automated tool doesn't even know either our ASM classes, or our implementation details, even exists. The only files that such tools (such as your IDE) sees are the types you're meant to see: lombok.Builder
, lombok.extern.slf4j.Slf4j
, lombok.experimental.UtilityClass
, etcetera.
How does it work
Java's classloader architecture is abstracted: You can make your own. The primitives offered by a class loader is simply this: "Convert this byte array containing bytecode (i.e. the contents of a class file) into a Class<?>
definition", and the primitives that you're supposed to implement when you write your own classloader is twofold:
- Here is a resource key, such as "/com/foo/load.png". Please provide me an InputStream with this data.
- Here is a fully qualified class name, such as "com.foo.MyApp". Please provide me with a
Class<?>
instance representing it.
Out of the box, java ships with a default classloader. This default classloader answers these questions by checking your CLASSPATH - which can be provided in various ways (via the jar manifest's Class-Path entry, or via the -cp
argument to the JVM executable, or the CLASSPATH environment variable), and scanning each entry on the classpath for the resource requested, capable of reading the file system as well as opening jar files.
But that's just a classloader. One implementation of the general principle that's baked into java. You can write your own. You can write a classloader that generates resources on the fly, or that loads them from a network.
Or, as lombok does, that loads them by opening its own jar and looking for .SCL.lombok
files.
Thus, lombok works like this: When you launch it, the 'entrypoint' (the class containing public static void main
- or in lombok's case, for javac mode it's the annotation processor entrypoint and for eclipse it's agentmain
), we 'hide' it from you using some fancy trickery: agentmain does not need to be in a public
class (it can't be .SCL.lombok
files - our classloader isn't available yet, we need to bootstrap that up first!). annotation processors do have to be in a public class, but, it's a public class inside a package private class, thus, just about every IDE knows it's 'invisible' and won't show it, but javac's annotation runner accepts it.
From there, we register a classloader that is capable of loading classes by way of reading in an .SCL.lombok
file, and this lets us hide everything else we want to hide.
I want to develop lombok and this is getting in the way!
No need; just clone our repo, run ant eclipse
or ant intellij
, and off you go. There is no way to extend lombok without first forking it; we'd like lombok to be able to be extensible without it, but that would be far more complicated than simply not doing the .SCL.lombok
thing. Eclipse runs on top of equinox, a runtime modularization system, and making that work properly requires all sorts of stuff that would make 'just toss some extra handlers on the classpath' not a feasible route to extending lombok in the first place.