Thursday, February 18, 2016

Java Lombok



https://www.alexecollins.com/5-tips-for-using-lombok-in-production/
Tip 1: No Lombok with logic
Tip 3: Use @Data for your DAOs
Where Lombok especially useful then? In DAOs. These objects typically don't have a lot of logic and a great deal of boilerplate. Specifically three annotations were the most useful.
@Data creates your getters, setters, to string and equals/hash code. Great for DAOs in either the database layer, or in the API layer.
Tip 4: Use @Value for immutable value-objects
@Value is essentially an immutable version of @Data. Very useful for immutable value-objects. Use in many of the cases you might use a Scala case class.
Tip 5: Use @Builder
@Builder is useful when you have an object with many fields with the same type. Rather than having a constructor with many string fields, use the builder instead.
Tip 6: Think about avoiding the other annotations
There are a number of annotations that we never found widely useful:
  • val - A great idea, but hobbled by poor IDE support.
  • @Cleanup - Use try-with-resources.
  • @SneakyThrows - Throw only runtime exceptions, and perform exception mapping where needed.
  • @Syncronized - Just never found a place to use this.
Tip 7: Exclude generated classes from Sonar report
As the generated code typically ends up with many un-tested methods (e.g. you never test the generated equals as you don't need to, but they tend to end up being very complex for classes with many fields). These classes are excluded for static analysis and code coverage. If you are using Maven and Sonar, you can do this using the sonar.exclusions property.
http://www.54tianzhisheng.cn/2018/01/09/lombok/

@Slf4j
注解在  上;为类提供一个 属性名为 log 的日志对象,提供默认构造方法
@AllArgsConstructor
注解在  上;为类提供一个全参的构造方法,加了这个注解后,类中不提供默认构造方法了。
@NoArgsConstructor
注解在  上;为类提供一个无参的构造方法。
@NonNull
注解在 属性 上,会自动产生一个关于此参数的非空检查,如果参数为空,则抛出一个空指针异常,也会有一个默认的无参构造方法。
@Cleanup
这个注解用在 变量 前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的 close() 方法,如果该资源有其它关闭方法,可使用 @Cleanup(“methodName”) 来指定要调用的方法,也会生成默认的构造方法
@RequiredArgsConstructor
这个注解用在  上,使用类中所有带有 @NonNull 注解的或者带有 final 修饰的成员变量生成对应的构造方法。
@Value
这个注解用在  上,会生成含所有参数的构造方法,get 方法,此外还提供了equals、hashCode、toString 方法。
@Synchronized
这个注解用在 类方法 或者 实例方法 上,效果和 synchronized 关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized 关键字的锁对象分别是类的 class 对象和 this 对象,而 @Synchronized 的锁对象分别是 私有静态 final 对象 lock 和 私有 final 对象 lock,当然,也可以自己指定锁对象,此外也提供默认的构造方法。

@EqualsAndHashCode(callSuper = true, of = {"id"})
https://groups.google.com/forum/#!topic/project-lombok/Xr13lPinsvg
@Data
class Address {
        Municipality municipality;
}
@Data
@ToString(exclude = "addresses")  // Without this we get
StackOverflowError
class Municipality {
        Set<Address> addresses = new HashSet<Address>();
        public void addAddress(Address a) {
                addresses.add(a);
        }
}
public class JavaTest {
        public static void main(String[] args) {
                Address address = new Address();
                Municipality municipality = new Municipality();
                municipality.addAddress(address);
                address.setMunicipality(municipality);
                System.out.println(municipality);
                System.out.println("Done.");
        }
}
http://stackoverflow.com/questions/32273060/javadoc-not-generated-for-lombok-getter-and-setter
The javadoc feature does not work in the eclipse javadoc view or hovers. You can generate the javadoc by running delombok on the code first and then run the javadoc compiler, as is hinted on the feature page near the bottom.

https://projectlombok.org/features/index.html






@Log4j2
Creates private static final org.apache.logging.log4j.Logger log = 
org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
Creates private static final org.slf4j.Logger log = 
org.slf4j.LoggerFactory.getLogger(LogExample.class);


On javadoc, and other source-based tools

Use delombok first, then run javadoc or GWT on the delombok-ed code.
https://projectlombok.org/features/NonNull.html
 public NonNullExample(@NonNull Person person) {
07     super("Hello");
08     if (person == null) {
09       throw new NullPointerException("person");
10     }
11     this.name = person.getName();
12   }
NEW in Lombok 0.11.10: You can use @NonNull on the parameter of a method or constructor to have lombok generate a null-check statement for you.

lombok.val
lombok.Value
lombok.Cleanup
lombok.Builder

How to Install:
.m2\repository\org\projectlombok\lombok\latest\lombok-latest.jar
https://www.credera.com/blog/technology-insights/java/project-lombok-learned-stop-worrying-love-autogeneration-annotations/
@Log and its variants are a safe and easy way to add loggers without copying and pasting the same logging declaration line between every class. Using this approach can cut down on copy/paste errors, such as leaving the wrong class name in a copied logger declaration line.
Lombok has always treated any annotation named @NonNull on a field as a signal to generate a null-check if lombok generates an entire method or constructor for you, via for example @Data. Now, however, using lombok's own @lombok.NonNull on a parameter results in the insertion of just the null-check statement inside your own method or constructor.
The null-check looks like if (param == null) throw new NullPointerException("param"); and will be inserted at the very top of your method. For constructors, the null-check will be inserted immediately following any explicit this() or super() calls.

https://projectlombok.org/features/val.html
07     val example = new ArrayList<String>();
07     final ArrayList<String> example = new ArrayList<String>();

Val can be especially useful when two imported packages have conflicting class names since it can prevent long package names from cluttering up method implementations. For example:
// class from another library with a conflicting name
val externalTime = new org.external.api.Time(); 
 
// class from this project with the same name
val internalTime = new Time();
May be preferable to reading:
org.external.api.Time externalTime = new org.external.api.Time();
Time internalTime = new Time();
http://jnb.ociweb.com/jnb/jnbJan2010.html
The @Data annotation is likely the most frequently used annotation in the Project Lombok toolset. It combines the functionality of @ToString@EqualsAndHashCode,@Getter and @Setter. Essentially, using @Data on a class is the same as annotating the class with a default @ToString and @EqualsAndHashCode as well as annotating each field with both @Getter and @Setter

The @Cleanup annotation can be used to ensure that allocated resources are released. When a local variable is annotated with @Cleanup, any subsequent code is wrapped in a try/finally block that guarantees that the cleanup method is called at the end of the current scope. By default @Cleanup assumes that the cleanup method is named "close", as with input and output streams

@Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();

Using the synchronized keyword on a method can result in unfortunate effects, as any developer who has worked on multi-threaded software can attest. The synchronized keyword will lock on the current object (this) in the case of an instance method or on the class object for a static method. This means that there is the potential for code outside of the control of the developer to lock on the same object, resulting in a deadlock. It is generally advisable to instead lock explicitly on a separate object that is dedicated solely to that purpose and not exposed in such a way as to allow unsolicited locking. Project Lombok provides the @Synchronizedannotation for that very purpose.


A look at the above code and the signature of Lombok.sneakyThrow(Throwable) would lead most to believe that the exception is being wrapped in a RuntimeExceptionand re-thrown, however this is not the case. The sneakyThrow method will never return normally and will instead throw the provided throwable completely unaltered.

Project Lombok provides the delombok utility for replacing the Lombok annotations with equivalent source code. This can be done for an entire source directory via the command line.
http://stackoverflow.com/questions/3418865/cannot-make-project-lombok-work-on-eclipse-helios/3425327#3425327

http://keaplogik.blogspot.com/2013/04/java-developers-need-to-be-using-lombok.html
  1. Never have to write getters and setters again.
public class Animal {
    @Getter @Setter private String name;
    @Getter @Setter private String gender;
    @Getter @Setter private String species;
}
https://projectlombok.org/features/
06     @Cleanup InputStream in = new FileInputStream(args[0]);
07     @Cleanup OutputStream out = new FileOutputStream(args[1]);
@Value is the immutable variant of @Data; all fields are made private and final by default, and setters are not generated. The class itself is also made final by default, because immutability is not something that can be forced onto a subclass. Like @Data, useful toString()equals() and hashCode() methods are also generated, each field gets a getter method, and a constructor that covers every argument (except finalfields that are initialized in the field declaration) is also generated.

@Builder lets you automatically produce the code required to have your class be instantiable with code such as:
Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build();

    By annotating one of the parameters (if annotating a method or constructor with @Builder) or fields (if annotating a class with @Builder) with the @Singular annotation, lombok will treat that builder node as a collection, and it generates 2 'adder' methods instead of a 'setter' method. One which adds a single element to the collection, and one which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated. A 'clear' method is also generated. These 'singular' builders are very complicated in order to guarantee the following properties:













    @SneakyThrows
    To boldly throw checked exceptions where no one has thrown them before!
    @Synchronized
    synchronized done right: Don't expose your locks.

    You can let lombok generate a getter which will calculate a value once, the first time this getter is called, and cache it from then on. This can be useful if calculating the value takes a lot of CPU, or the value takes a lot of memory. To use this feature, create a private final variable, initialize it with the expression that's expensive to run, and annotate your field with @Getter(lazy=true). The field will be hidden from the rest of your code, and the expression will be evaluated no more than once, when the getter is first called. There are no magic marker values (i.e. even if the result of your expensive calculation is null, the result is cached) and your expensive calculation need not be thread-safe, as lombok takes care of locking.

    04   @Getter(lazy=trueprivate final double[] cached = expensive();
    05   
    06   private double[] expensive() {
    07     double[] result = new double[1000000];
    08     for (int i = 0; i < result.length; i++) {
    09       result[i= Math.asin(i);
    10     }
    11     return result;
    12   }













    @Log
    Creates private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
    @Log4j
    Creates private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
    @Log4j2
    Creates private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
    @Slf4j
    Creates private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
    http://mvnrepository.com/artifact/org.projectlombok/lombok

    https://github.com/rzwitserloot/lombok/issues/864
    Never mind! It is my fault I renamed Eclipse.app, that is the reason it does not find my Mars installation

    http://stackoverflow.com/questions/6853053/how-to-run-eclipse-clean-on-a-mac
    If you CD to the eclipse installation directory using terminal, then you will see there is a directory called eclipse.app. CD to Eclipse.app\Contents\MacOS under that directory there should be an executable called eclipse.
    I believe you can launch eclipse from the commandline by executing the eclipse executable with the -clean argument, as below:
    ./eclipse -clean
    eclipse/4.5m6/Eclipse.app/Contents/MacOS/eclipse -clean

    Issues
    http://stackoverflow.com/questions/3852091/is-it-safe-to-use-project-lombok
    UPDATE 3 Learning to use the various ways of doing things in Eclipse I guess. You can actually set a conditional breakpoint (BP) on a Lombok generated method. Using the Outline view, you can right-click the method to Toggle Method Breakpoint. Then when you hit the BP, you can use the debugging Variables view to see what the generated method named the parameters (usually the same as the field name) and finally, use the Breakpoints view to right-click the BP and select Breakpoint Properties... to add a condition. Nice.
    UPDATE 7 This is a bit of an interesting update because it directly addresses the safety of adopting Lombok that the OP asked about.
    As of v1.14, the @Delegate annotation has been demoted to an Experimental status. The details are documented on their site (Lombok Delegate Docs).
    The thing is, if you were using this feature, your backout options are limited. I see the options as:
    • Manually remove @Delegate annotations and generate/handcode the delegate code. This is a little harder if you were using attributes within the annotation.
    • Delombok the files that have the @Delegate annotation and maybe add back in the annotations that you do want.
    • Never update Lombok or maintain a fork.
    • Delombok your entire project and stop using Lombok.

    UPDATE 12 While trying to come up with justifications for why it's safe to bring in Lombok for the project I'm currently working on, I found a piece of gold that was added with v1.14 - TheConfiguration System! This is means you can configure a project to dis-allow certain features that your team deems unsafe or undesirable. Better yet, it can also create directory specific config with different settings. This is AWESOME.
    Usually, a user of lombok puts a lombok.config file with their preferences in a workspace or project root directory, with the special config.stopBubbling = true key to tell lombok this is your root directory. You can then create lombok.config files in any subdirectories (generally representing projects or source packages) with different settings.












    lombok.accessors.chain
    If set to true, generated setters will 'chain' by default (They will return this instead of having a void return type).
    lombok.accessors.fluent
    If set to true, generated setters and getters will simply be named the same as the field name, without a get or set prefix.
    lombok.log.fieldName
    The name of the generated log field (default: log).
    lombok.(featureName).flagUsage
    Allows you to forcibly stop or discourage use of a lombok feature. Legal values for this key are warning or error. Some examples of values for (featureName) are: "experimental" (flags use of any of theexperimental features), "builder", "sneakyThrows", or "extensionMethod".
    To stop lombok from looking at parent directories for more configuration files, the special key:
    config.stopBubbling = true
    can be included. We suggest you put this in the root of your workspace directory.
    Lombok normally adds @javax.annotation.Generated annotations to all generated nodes where possible. You can stop this with:
    lombok.addGeneratedAnnotation = false

    Lombok can add the @SuppressFBWarnings annotation which is useful if you want to run FindBugs on your class files. To enable this feature, make sure findbugs is on the classpath when you compile, and add the following config key:
    lombok.extern.findbugs.addSuppressFBWarnings = true
    Alternatives
    Maven also required the following snippet to be added to the maven-compiler-plugin’s <configuration>section.
    <annotationProcessors>
        <annotationProcessor>com.google.auto.value.processor.AutoValueProcessor</annotationProcessor>
    </annotationProcessors>
    
    There was also a weird issue I’m blaming on Eclipse. Even with the correct config files, when I imported the project, I needed to turn annotation processing off and on again to get it to work. Nasty.

    Without equals and hashCode, you can never...
    put it in a HashSet
    use it as a key in a HashMap, Cache, etc.
    put it in any collection and have contains work
    use assertEquals with it in tests.... etc.
    Without toString, logging and debugging are painful.

    Quite simply, if something feels like a value object, it's generally considered evil to not include all this stuff.

    Suppose a required int field becomes optional. You change
    int number;
    to
    Integer number;
    Tests still pass... and everything's fine... until one day, objects put into Sets start mysteriously "disappearing" from the set! Why? (Left as an exercise to the reader.)
    And there are also second-order costs:
    Bugs caused by not bothering to do all that in your value types
    Avoidance of even creating new types in the first plac

    Imagine abstract base classes like Tuple2<A,B>, Tuple3<A,B,C>, etc.
    The inserted code is invisible. This makes for a poor experience with tools, such as debuggers and code explorers.
    The compiler hacks are non-standard and fragile.
    In our view, your code is no longer really Java (Lombok is very "extralinguistic")

    You write an abstract class
    It has abstract accessors, but no fields
    Annotate it with @AutoValue
    Javac generates a concrete subclass for you
    Callers only ever see the parent type

    @AutoValue
    public abstract class Foo {
      public static Foo create(String text, int number) {
        // defensive copies, preconditions
        return new AutoValue_Foo(text, number);
      }

      /** Documentation here. */
      public abstract String text(); // or getText(), if you like

      /** Documentation here. */
      public abstract int number();
    }

    User writes only plain old Java code
    No runtime impact
    no dependency (@AutoValue has source retention)
    performs comparably to hand-written code
    (1-morphic, so accessors are still inlinable)
    Virtually no impact on API
    Exception: if you already committed to a public constructor, you can't switch to this
    No magical modifying of existing classes
    Still just a single javac pass to compile!

    AutoValue does introduce some fragility.

    The generator has to choose the order of constructor parameters somehow, so it uses the order in which the accessors appear in the source file.

    This means an innocent refactoring to reorder those accessors could break your tests. (You do have tests that actually do stuff with your value objects, right?)

    https://projectlombok.org/features/experimental/Accessors.html


    @Accessors therefore has 3 options:
    • fluent - A boolean. If true, the getter for pepper is just pepper(), and the setter is pepper(T newValue). Furthermore, unless specified, chain defaults to true.
      Default: false.
    • chain - A boolean. If true, generated setters return this instead of void.
      Default: false, unless fluent=true, then Default: true.
    • prefix - A list of strings. If present, fields must be prefixed with any of these prefixes. Each field name is compared to each prefix in the list in turn, and if a match is found, the prefix is stripped out to create the base name for the field. It is legal to include an empty string in the list, which will always match. For characters which are letters, the character following the prefix must not be a lowercase letter, i.e. pepper is not a match even to prefix p, but pEpper would be (and would mean the base name of this field is epper).

    • The @Accessors annotation is legal on types and fields; the annotation that applies is the one on the field if present, otherwise the one on the class. When a @Accessors annotation on a field is present, any@Accessors annotation also present on that field's type is ignored.
    https://projectlombok.org/features/GetterSetter.html



    lombok.accessors.chain = [true | false] (default: false)
    If set to true, generated setters will return this (instead of void). An explicitly configured chain parameter of an @Accessors annotation takes precedence over this setting.
    lombok.accessors.fluent = [true | false] (default: false)
    If set to true, generated getters and setters will not be prefixed with the bean-standard 'getis or set; instead, the methods will use the same name as the field (minus prefixes). An explicitly configured chainparameter of an @Accessors annotation takes precedence over this setting.

    https://blog.mythsman.com/2017/12/19/1/
    但是,我们发现这个包跟一般的包有很大区别,绝大多数java包都工作在运行时,比如spring提供的那种注解,通过在运行时用反射来实现业务逻辑。Lombok这个东西工作却在编译期,在运行时是无法通过反射获取到这个注解的。
    翻了翻现有的资料,再加上自己的一些猜想,Lombok的基本流程应该基本是这样:
    • 定义编译期的注解
    • 利用JSR269 api(Pluggable Annotation Processing API )创建编译期的注解处理器
    • 利用tools.jar的javac api处理AST(抽象语法树)
    • 将功能注册进jar包
    <dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>${java.home}/../lib/tools.jar</systemPath>
    </dependency>


    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Getter {
    }

    创建Getter注解

    这里的Target我选择了ElementType.TYPE表示是对类的注解,Retention选择了RententionPolicy.SOURCE,表示这个注解只在编译期起作用,在运行时将不存在。这个比较简单,稍微复杂点的是对这个注解的处理机制。像spring那种注解是通过反射来获得注解对应的元素并实现业务逻辑,但是我们显然不希望在使用Lombok这种功能的时候还要编写其他的调用代码,况且用反射也获取不到编译期才存在的注解。
    幸运的是Java早已支持了JSR269的规范,允许在编译时指定一个processor类来对编译阶段的注解进行干预


    @SupportedAnnotationTypes("com.mythsman.test.Getter")
    @SupportedSourceVersion(SourceVersion.RELEASE_8)
    public class GetterProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    }
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    return true;
    }
    }

    需要定义两个注解,一个表示该处理器需要处理的注解,另外一个表示该处理器支持的源码版本。然后需要着重实现两个方法,init跟process。init的主要用途是通过ProcessingEnvironment来获取编译阶段的一些环境信息;process主要是实现具体逻辑的地方,也就是对AST进行处理的地方
    那么如何在调用的时候不用加参数呢,其实我们知道java在编译的时候会去资源文件夹下读一个META-INF文件夹,这个文件夹下面除了MANIFEST.MF文件之外,还可以添加一个services文件夹,我们可以在这个文件夹下创建一个文件,文件名是javax.annotation.processing.Processor,文件内容是com.mythsman.test.GetterProcessor。
    我们知道maven在编译前会先拷贝资源文件夹,然后当他在编译时候发现了资源文件夹下的META-INF/serivces文件夹时,他就会读取里面的文件,并将文件名所代表的接口用文件内容表示的类来实现。这就相当于做了-processor参数该做的事了。
    当然这个文件我们并不希望调用者去写,而是希望在processor项目里集成,调用的时候能直接继承META-INF。

    Labels

    Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

    Popular Posts