SpringBoot e-commerce project mall (40K + STAR) address: github.com/macrozheng/…

Abstract

Recently, the last version of IDEA 2020 was released with the Lombok plug-in built in, and SpringBoot 2.1.x and later versions have Lombok dependencies built into the Starter. Why should they all support Lombok? Today I’m going to talk about using Lombok and see what’s amazing about it!

Introduction of Lombok

Lombok is a Java code enhancement library available on Github at 9.8K +Star. It automatically integrates into your editors and build tools to make your Java code more interesting. With Lombok annotations, you don’t have to write getters, setters, equals, etc. Lombok will automatically generate them for you at compile time.

Lombok integration

If you are using the latest version of IDEA 2020.3, Lombok is already built in and you do not need to install it.

Lombok does not need to be specified after SpringBoot 2.1.x. SpringBoot is already built in SpringBoot – Dependencies.

<! - lombok dependence - >
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
Copy the code

Lombok use

There are many annotations in Lombok that make it easier to write Java code. Here’s how to use them.

val

Using val annotations can replace any type as a local variable, so we don’t have to write complex ArrayList and Map.entry types, as shown in the following example.

/** * Created by macro on 2020/12/16. */
public class ValExample {

    public static void example(a) {
        //val replaces ArrayList
      
        and String types
      
        val example = new ArrayList<String>();
        example.add("Hello World!");
        val foo = example.get(0);
        System.out.println(foo.toLowerCase());
    }

    public static void example2(a) {
        // Val replaces map. Entry
      ,string>
        val map = new HashMap<Integer, String>();
        map.put(0."zero");
        map.put(5."five");
        for (val entry : map.entrySet()) {
            System.out.printf("%d: %s\n", entry.getKey(), entry.getValue()); }}public static void main(String[] args) { example(); example2(); }}Copy the code

When we use the val annotation, Lombok will infer the specific type from the initialization expression of the local variable, which when compiled generates the following code.

public class ValExample {
    public ValExample(a) {}public static void example(a) {
        ArrayList<String> example = new ArrayList();
        example.add("Hello World!");
        String foo = (String)example.get(0);
        System.out.println(foo.toLowerCase());
    }

    public static void example2(a) {
        HashMap<Integer, String> map = new HashMap();
        map.put(0."zero");
        map.put(5."five");
        Iterator var1 = map.entrySet().iterator();

        while(var1.hasNext()) {
            Entry<Integer, String> entry = (Entry)var1.next();
            System.out.printf("%d: %s\n", entry.getKey(), entry.getValue()); }}}Copy the code

@NonNull

Using the @nonNULL annotation on a method can make a non-null judgment and throw a NullPointerException if a null value is passed in.

/** * Created by macro on 2020/12/16. */
public class NonNullExample {
    private String name;
    public NonNullExample(@NonNull String name){
        this.name = name;
    }

    public static void main(String[] args) {
        new NonNullExample("test");
        // NullPointerException is thrown
        new NonNullExample(null); }}Copy the code

Non-null judgments are added to the constructor after compilation, as shown below.

public class NonNullExample {
    private String name;

    public NonNullExample(@NonNull String name) {
        if (name == null) {
            throw new NullPointerException("name is marked non-null but is null");
        } else {
            this.name = name; }}public static void main(String[] args) {
        new NonNullExample("test");
        new NonNullExample((String)null); }}Copy the code

@Cleanup

When we use resources in Java, we inevitably need to close the resources after use. Use the @cleanup annotation to automatically close the resource.

/** * Created by macro on 2020/12/16. */
public class CleanupExample {
    public static void main(String[] args) throws IOException {
        String inStr = "Hello World!";
        // Use input and output streams to close automatically, without writing a try catch and calling the close() method
        @Cleanup ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes("UTF-8"));
        @Cleanup ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        while (true) {
            int r = in.read(b);
            if (r == -1) break;
            out.write(b, 0, r);
        }
        String outStr = out.toString("UTF-8"); System.out.println(outStr); }}Copy the code

Lombok generates the following code when compiled.

public class CleanupExample {
    public CleanupExample(a) {}public static void main(String[] args) throws IOException {
        String inStr = "Hello World!";
        ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes("UTF-8"));

        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();

            try {
                byte[] b = new byte[1024];

                while(true) {
                    int r = in.read(b);
                    if (r == -1) {
                        String outStr = out.toString("UTF-8");
                        System.out.println(outStr);
                        return;
                    }

                    out.write(b, 0, r); }}finally {
                if (Collections.singletonList(out).get(0) != null) { out.close(); }}}finally {
            if (Collections.singletonList(in).get(0) != null) { in.close(); }}}}Copy the code

@Getter/@Setter

With the @getter / @setter annotation, we don’t have to write Getter/ Setter methods anymore. Even if we used IDEA to generate getter/setter methods automatically, it would be a hassle to generate getter/setter methods again if the type and name of the class property changed.

/** * Created by macro on 2020/12/17. */
public class GetterSetterExample {
    @Getter
    @Setter
    private String name;
    @Getter
    @Setter(AccessLevel.PROTECTED)
    private Integer age;

    public static void main(String[] args) {
        GetterSetterExample example = new GetterSetterExample();
        example.setName("test");
        example.setAge(20);
        System.out.printf("name:%s age:%d",example.getName(),example.getAge()); }}Copy the code

Lombok generates the following code when compiled.

public class GetterSetterExample {
    private String name;
    private Integer age;

    public GetterSetterExample(a) {}public String getName(a) {
        return this.name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public Integer getAge(a) {
        return this.age;
    }

    protected void setAge(final Integer age) {
        this.age = age; }}Copy the code

@ToString

How tedious it would be to write all the class attributes into the toString method for logging. Use the @toString annotation to automatically generate ToString methods, which by default include all class attributes. Use the @toString.Exclude annotation to Exclude attribute generation.

/** * Created by macro on 2020/12/17. */
@ToString
public class ToStringExample {
    @ToString.Exclude
    private Long id;
    private String name;
    private Integer age;
    public ToStringExample(Long id,String name,Integer age){
        this.id =id;
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        ToStringExample example = new ToStringExample(1L."test".20);
        ToStringExample(name=test, age=20)System.out.println(example); }}Copy the code

Lombok generates the following code when compiled.

public class ToStringExample {
    private Long id;
    private String name;
    private Integer age;

    public ToStringExample(Long id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public String toString(a) {
        return "ToStringExample(name=" + this.name + ", age=" + this.age + ")"; }}Copy the code

@EqualsAndHashCode

Using the @ EqualsAndHashCode annotations can automatically generate the equals and hashCode methods, the default contains all the class attribute, use the @ EqualsAndHashCode. Exclude the generation of attributes can be ruled out.

/** * Created by macro on 2020/12/17. */
@Getter
@Setter
@EqualsAndHashCode
public class EqualsAndHashCodeExample {
    private Long id;
    @EqualsAndHashCode.Exclude
    private String name;
    @EqualsAndHashCode.Exclude
    private Integer age;

    public static void main(String[] args) {
        EqualsAndHashCodeExample example1 = new EqualsAndHashCodeExample();
        example1.setId(1L);
        example1.setName("test");
        example1.setAge(20);
        EqualsAndHashCodeExample example2 = new EqualsAndHashCodeExample();
        example2.setId(1L);
        // Equals only compares ids and returns trueSystem.out.println(example1.equals(example2)); }}Copy the code

Lombok generates the following code when compiled.

public class EqualsAndHashCodeExample {
    private Long id;
    private String name;
    private Integer age;

    public EqualsAndHashCodeExample(a) {}public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if(! (oinstanceof EqualsAndHashCodeExample)) {
            return false;
        } else {
            EqualsAndHashCodeExample other = (EqualsAndHashCodeExample)o;
            if(! other.canEqual(this)) {
                return false;
            } else {
                Object this$id = this.getId();
                Object other$id = other.getId();
                if (this$id == null) {
                    if(other$id ! =null) {
                        return false; }}else if (!this$id.equals(other$id)) {
                    return false;
                }

                return true; }}}protected boolean canEqual(final Object other) {
        return other instanceof EqualsAndHashCodeExample;
    }

    public int hashCode(a) {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        returnresult; }}Copy the code

@XxConstructor

Constructors can be generated automatically using the @xxConstructor annotation, with the @noargsConstructor, @requiredargsConstructor, and @AllargsConstructor annotations available.

  • NoArgsConstructor: Generates a no-argument constructor.
  • RequiredArgsConstructor: Generates a constructor containing required arguments, using the class attribute of the @nonnull annotation as required arguments.
  • @allargsconstructor: Generates a constructor that contains all arguments.
/** * Created by macro on 2020/12/17. */
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor
public class ConstructorExample {
    @NonNull
    private Long id;
    private String name;
    private Integer age;

    public static void main(String[] args) {
        // No parameter constructor
        ConstructorExample example1 = new ConstructorExample();
        // All arguments constructor
        ConstructorExample example2 = new ConstructorExample(1L."test".20);
        // mandatory argument constructor for @nonnull annotation
        ConstructorExample example3 = ConstructorExample.of(1L); }}Copy the code

Lombok generates the following code when compiled.

public class ConstructorExample {
    @NonNull
    private Long id;
    private String name;
    private Integer age;

    public ConstructorExample(a) {}private ConstructorExample(@NonNull final Long id) {
        if (id == null) {
            throw new NullPointerException("id is marked non-null but is null");
        } else {
            this.id = id; }}public static ConstructorExample of(@NonNull final Long id) {
        return new ConstructorExample(id);
    }

    public ConstructorExample(@NonNull final Long id, final String name, final Integer age) {
        if (id == null) {
            throw new NullPointerException("id is marked non-null but is null");
        } else {
            this.id = id;
            this.name = name;
            this.age = age; }}}Copy the code

@Data

@data is a handy composite annotation that combines @toString, @equalSandHashCode, @getter, @setter, and @requiredargsConstructor.

/** * Created by macro on 2020/12/17. */
@Data
public class DataExample {
    @NonNull
    private Long id;
    @EqualsAndHashCode.Exclude
    private String name;
    @EqualsAndHashCode.Exclude
    private Integer age;

    public static void main(String[] args) {
        / / @ RequiredArgsConstructor has to take effect
        DataExample example1 = new DataExample(1L);
        // @getter@setter is in effect
        example1.setName("test");
        example1.setAge(20);
        / / @ ToString has to take effect
        System.out.println(example1);
        DataExample example2 = new DataExample(1L);
        / / @ EqualsAndHashCode has to take effectSystem.out.println(example1.equals(example2)); }}Copy the code

Lombok generates the following code when compiled.

public class DataExample {
    @NonNull
    private Long id;
    private String name;
    private Integer age;

    public DataExample(@NonNull final Long id) {
        if (id == null) {
            throw new NullPointerException("id is marked non-null but is null");
        } else {
            this.id = id; }}@NonNull
    public Long getId(a) {
        return this.id;
    }

    public String getName(a) {
        return this.name;
    }

    public Integer getAge(a) {
        return this.age;
    }

    public void setId(@NonNull final Long id) {
        if (id == null) {
            throw new NullPointerException("id is marked non-null but is null");
        } else {
            this.id = id; }}public void setName(final String name) {
        this.name = name;
    }

    public void setAge(final Integer age) {
        this.age = age;
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if(! (oinstanceof DataExample)) {
            return false;
        } else {
            DataExample other = (DataExample)o;
            if(! other.canEqual(this)) {
                return false;
            } else {
                Object this$id = this.getId();
                Object other$id = other.getId();
                if (this$id == null) {
                    if(other$id ! =null) {
                        return false; }}else if (!this$id.equals(other$id)) {
                    return false;
                }

                return true; }}}protected boolean canEqual(final Object other) {
        return other instanceof DataExample;
    }

    public int hashCode(a) {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        return result;
    }

    public String toString(a) {
        return "DataExample(id=" + this.getId() + ", name=" + this.getName() + ", age=" + this.getAge() + ")"; }}Copy the code

@Value

Using the @Value annotation, you can declare a class immutable. When you declare a class, it is final, cannot be inherited, and its properties become final.

/** * Created by macro on 2020/12/17. */
@Value
public class ValueExample {
    private Long id;
    private String name;
    private Integer age;

    public static void main(String[] args) {
        // Only full-parameter constructors can be used
        ValueExample example = new ValueExample(1L."test".20);
        // example.setname (" Andy ") // No setter is generated, an error is reported
        // example.name=" Andy "// An error occurs when the field is set to final}}Copy the code

Lombok generates the following code when compiled.

public final class ValueExample {
    private final Long id;
    private final String name;
    private final Integer age;

    public static void main(String[] args) {
        new ValueExample(1L."test".20);
    }

    public ValueExample(final Long id, final String name, final Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Long getId(a) {
        return this.id;
    }

    public String getName(a) {
        return this.name;
    }

    public Integer getAge(a) {
        return this.age; }}Copy the code

@Builder

Using the @Builder annotation, you can create objects from Builder mode. Builder mode is called by chain. It’s so easy to create objects!

/** * Created by macro on 2020/12/17. */
@Builder
@ToString
public class BuilderExample {
    private Long id;
    private String name;
    private Integer age;

    public static void main(String[] args) {
        BuilderExample example = BuilderExample.builder()
                .id(1L)
                .name("test")
                .age(20) .build(); System.out.println(example); }}Copy the code

Lombok generates the following code when compiled.

public class BuilderExample {
    private Long id;
    private String name;
    private Integer age;

    BuilderExample(final Long id, final String name, final Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public static BuilderExample.BuilderExampleBuilder builder(a) {
        return new BuilderExample.BuilderExampleBuilder();
    }

    public String toString(a) {
        return "BuilderExample(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")";
    }

    public static class BuilderExampleBuilder {
        private Long id;
        private String name;
        private Integer age;

        BuilderExampleBuilder() {
        }

        public BuilderExample.BuilderExampleBuilder id(final Long id) {
            this.id = id;
            return this;
        }

        public BuilderExample.BuilderExampleBuilder name(final String name) {
            this.name = name;
            return this;
        }

        public BuilderExample.BuilderExampleBuilder age(final Integer age) {
            this.age = age;
            return this;
        }

        public BuilderExample build(a) {
            return new BuilderExample(this.id, this.name, this.age);
        }

        public String toString(a) {
            return "BuilderExample.BuilderExampleBuilder(id=" + this.id + ", name=" + this.name + ", age=" + this.age + ")"; }}}Copy the code

@SneakyThrows

Still catching and throwing exceptions manually? Try using @sneakythrows to automatically implement annotations!

/** * Created by macro on 2020/12/17. */
public class SneakyThrowsExample {

    // Automatically throws an exception, no action required
    @SneakyThrows(UnsupportedEncodingException.class)
    public static byte[] str2byte(String str){
        return str.getBytes("UTF-8");
    }

    public static void main(String[] args) {
        String str = "Hello World!"; System.out.println(str2byte(str).length); }}Copy the code

Lombok generates the following code when compiled.

public class SneakyThrowsExample {
    public SneakyThrowsExample(a) {}public static byte[] str2byte(String str) {
        try {
            return str.getBytes("UTF-8");
        } catch (UnsupportedEncodingException var2) {
            throwvar2; }}}Copy the code

@Synchronized

Thread-safety issues often arise when accessing the same resource in multiple threads. Previously, we used the synchronized keyword modifier to achieve synchronized access. Using the @synchronized annotation can also achieve synchronous access.

package com.macro.mall.tiny.example;

import lombok.*;

/** * Created by macro on 2020/12/17. */
@Data
public class SynchronizedExample {
    @NonNull
    private Integer count;

    @Synchronized
    @SneakyThrows
    public void reduceCount(Integer id) {
        if (count > 0) {
            Thread.sleep(500);
            count--;
            System.out.println(String.format("thread-%d count:%d", id, count)); }}public static void main(String[] args) {
        // Add @synchronized Three threads can invoke the reduceCount method simultaneously
        SynchronizedExample example = new SynchronizedExample(20);
        new ReduceThread(1, example).start();
        new ReduceThread(2, example).start();
        new ReduceThread(3, example).start();
    }


    @RequiredArgsConstructor
    static class ReduceThread extends Thread {
        @NonNull
        private Integer id;
        @NonNull
        private SynchronizedExample example;

        @Override
        public void run(a) {
            while (example.getCount() > 0) { example.reduceCount(id); }}}}Copy the code

Lombok generates the following code when compiled.

public class SynchronizedExample {
    private final Object $lock = new Object[0];
    @NonNull
    private Integer count;

    public void reduceCount(Integer id) {
        try {
            synchronized(this.$lock) {
                if (this.count > 0) {
                    Thread.sleep(500L);
                    Integer var3 = this.count;
                    Integer var4 = this.count = this.count - 1;
                    System.out.println(String.format("thread-%d count:%d", id, this.count)); }}}catch (Throwable var7) {
            throwvar7; }}}Copy the code

@With

Using the @with annotation, you can clone an object and change one of its properties by specifying a full-parameter constructor.

@With
@AllArgsConstructor
public class WithExample {
    private Long id;
    private String name;
    private Integer age;

    public static void main(String[] args) {
        WithExample example1 = new WithExample(1L."test".20);
        WithExample example2 = example1.withAge(22);
        // Clone the old object and set age to falseSystem.out.println(example1.equals(example2)); }}Copy the code

Lombok generates the following code when compiled.

public class WithExample {
    private Long id;
    private String name;
    private Integer age;

    public WithExample withId(final Long id) {
        return this.id == id ? this : new WithExample(id, this.name, this.age);
    }

    public WithExample withName(final String name) {
        return this.name == name ? this : new WithExample(this.id, name, this.age);
    }

    public WithExample withAge(final Integer age) {
        return this.age == age ? this : new WithExample(this.id, this.name, age);
    }

    public WithExample(final Long id, final String name, final Integer age) {
        this.id = id;
        this.name = name;
        this.age = age; }}Copy the code

@Getter(lazy=true)

When we get a property that consumes resources, we can add lazy=true to the @getter to implement lazy loading. The Double Check Lock boilerplate code is generated for lazy loading of the property.

/** * Created by macro on 2020/12/17. */
public class GetterLazyExample {
    @Getter(lazy = true)
    private final double[] cached = expensive();

    private double[] expensive() {
        double[] result = new double[1000000];
        for (int i = 0; i < result.length; i++) {
            result[i] = Math.asin(i);
        }
        return result;
    }

    public static void main(String[] args) {
        // Lazy loading of properties using Double Check Lock boilerplate code
        GetterLazyExample example = newGetterLazyExample(); System.out.println(example.getCached().length); }}Copy the code

Lombok generates the following code when compiled.

public class GetterLazyExample {
    private final AtomicReference<Object> cached = new AtomicReference();

    public GetterLazyExample(a) {}private double[] expensive() {
        double[] result = new double[1000000];

        for(int i = 0; i < result.length; ++i) {
            result[i] = Math.asin((double)i);
        }

        return result;
    }

    public double[] getCached() {
        Object value = this.cached.get();
        if (value == null) {
            synchronized(this.cached) {
                value = this.cached.get();
                if (value == null) {
                    double[] actualValue = this.expensive();
                    value = actualValue == null ? this.cached : actualValue;
                    this.cached.set(value); }}}return (double[]) ((double[])(value == this.cached ? null: value)); }}Copy the code

@Log

Using the @log annotation, you can directly generate the Log object Log, from which you can directly print the Log.

/** * Created by macro on 2020/12/17. */
@Log
public class LogExample {
    public static void main(String[] args) {
        log.info("level info");
        log.warning("level warning");
        log.severe("level severe"); }}Copy the code

Lombok generates the following code when compiled.

public class LogExample {
    private static final Logger log = Logger.getLogger(LogExample.class.getName());

    public LogExample(a) {}public static void main(String[] args) {
        log.info("level info");
        log.warning("level warning");
        log.severe("level severe"); }}Copy the code

@Slf4j

When generating log objects using Lombok, there are several annotations available, depending on the logging implementation used. For example, @log, @log4j, @log4j2, @slf4j and so on.

/** * Created by macro on 2020/12/17. */
@Slf4j
public class LogSlf4jExample {
    public static void main(String[] args) {
        log.info("level:{}"."info");
        log.warn("level:{}"."warn");
        log.error("level:{}"."error"); }}Copy the code

Lombok generates the following code when compiled.

public class LogSlf4jExample {
    private static final Logger log = LoggerFactory.getLogger(LogSlf4jExample.class);

    public LogSlf4jExample(a) {}public static void main(String[] args) {
        log.info("level:{}"."info");
        log.warn("level:{}"."warn");
        log.error("level:{}"."error"); }}Copy the code

Lombok principle

If Lombok is not installed for IDEA, our project using Lombok will not compile. After installing it, IDEA will remind us of the methods and properties Lombok generated for us.

With the @data annotation, you can look at the class structure to find getters, setters, toString, and so on.

By opening the.class file in the Target directory, we can see the code Lombok has generated for us, showing that Lombok has enhanced Java code by parsing annotations and then generating code at compile time.

The resources

The official document: projectlombok.org/features/al…

Project source code address

Github.com/macrozheng/…

In this paper, making github.com/macrozheng/… Already included, welcome everyone Star!