Какая кожа у Ломбока? И SpringBoot, и IDEA официально его поддерживают!

Spring Boot Java
Какая кожа у Ломбока? И SpringBoot, и IDEA официально его поддерживают!

Адрес фактического центра электронной коммерции SpringBoot (40k+star):GitHub.com/macro-positive/…

Резюме

недавнийВыпущена последняя версия IDEA 2020, подключаемый модуль Lombok был встроен, а версии после SpringBoot 2.1.x также имеют зависимости Lombok, встроенные в Starter. Почему они все поддерживают Ломбока? Сегодня я расскажу об использовании Ломбока и увижу, насколько это потрясающе!

Знакомство с Ломбоком

Lombok — это библиотека улучшения кода Java, имеющая 9,8 тыс. звезд на Github. Он автоматически интегрируется в ваш редактор и инструменты сборки, чтобы сделать ваш Java-код более интересным. Благодаря аннотациям Lombok вы больше не можете писать геттеры, сеттеры, равные и другие методы, Lombok автоматически сгенерирует их для вас во время компиляции.

Ломбокская интеграция

Во-первых, нам нужно установить подключаемый модуль Lombok в IDEA.Если вы используете последнюю версию IDEA 2020.3, подключаемый модуль Lombok уже встроен и его не нужно устанавливать.

Затем добавьте зависимость Lombok в файл pom.xml проекта После версии SpringBoot 2.1.x версию Lombok указывать не нужно.spring-boot-dependenciesуже встроен.

<!--lombok依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

Использование Ломбока

В Ломбоке есть много аннотаций, которые облегчают нам написание Java-кода. Ниже описано использование этих аннотаций.

val

Использование аннотации val может заменить любой тип в качестве локальной переменной, поэтому нам не нужно писать сложные типы ArrayList и Map.Entry Конкретный пример выглядит следующим образом.

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

    public static void example() {
        //val代替ArrayList<String>和String类型
        val example = new ArrayList<String>();
        example.add("Hello World!");
        val foo = example.get(0);
        System.out.println(foo.toLowerCase());
    }

    public static void example2() {
        //val代替Map.Entry<Integer,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();
    }
}

Когда мы используем аннотацию val, Lombok выведет конкретный тип из выражения инициализации локальной переменной, и после компиляции будет сгенерирован следующий код.

public class ValExample {
    public ValExample() {
    }

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

    public static void example2() {
        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());
        }

    }
}

@NonNull

Используйте аннотацию @NonNull в методе, чтобы сделать ненулевое суждение.Если передано нулевое значение, будет напрямую выдано исключение NullPointerException.

/**
 * 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
        new NonNullExample(null);
    }
}

После компиляции в конструктор будет добавлено непустое суждение.Конкретный код выглядит следующим образом.

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);
    }
}

@Cleanup

Когда мы используем ресурсы в Java, нам неизбежно нужно закрывать ресурсы после использования. Используйте аннотацию @Cleanup для автоматического закрытия ресурсов.

/**
 * Created by macro on 2020/12/16.
 */
public class CleanupExample {
    public static void main(String[] args) throws IOException {
        String inStr = "Hello World!";
        //使用输入输出流自动关闭,无需编写try catch和调用close()方法
        @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);
    }
}

После компиляции Lombok сгенерирует следующий код.

public class CleanupExample {
    public CleanupExample() {
    }

    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();
            }

        }
    }
}

@Getter/@Setter

С аннотациями @Getter/@Setter нам больше не нужно писать методы получения/установки. Представьте, что даже если бы мы использовали IDEA для автоматического создания методов получения/установки, если бы тип и имя атрибута класса изменились, было бы очень проблематично перегенерировать метод получения/установки.

/**
 * 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());
    }
}

После компиляции Lombok сгенерирует следующий код.

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

    public GetterSetterExample() {
    }

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

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

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

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

@ToString

Запись всех свойств класса в метод toString для облегчения печати журналов — такая утомительная вещь. Используйте аннотацию @ToString для автоматического создания метода toString, который по умолчанию будет включать все атрибуты класса, и используйте аннотацию @ToString.Exclude, чтобы исключить создание атрибутов.

/**
 * 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);
        //自动实现toString方法,输出ToStringExample(name=test, age=20)
        System.out.println(example);
    }
}

После компиляции Lombok сгенерирует следующий код.

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() {
        return "ToStringExample(name=" + this.name + ", age=" + this.age + ")";
    }
}

@EqualsAndHashCode

Используйте аннотацию @EqualsAndHashCode для автоматического создания методов hashCode и equals, которые по умолчанию включают все атрибуты класса, и используйте @EqualsAndHashCode.Exclude, чтобы исключить создание атрибутов.

/**
 * 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方法只对比id,返回true
        System.out.println(example1.equals(example2));
    }
}

После компиляции Lombok сгенерирует следующий код.

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

    public EqualsAndHashCodeExample() {
    }

    public boolean equals(final Object o) {
        if (o == this) {
            return true;
        } else if (!(o instanceof 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() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        return result;
    }
}

@XxConstructor

Конструкторы могут быть созданы автоматически с помощью аннотации @XxConstructor.Существуют три аннотации @NoArgsConstructor, @RequiredArgsConstructor и @AllArgsConstructor, которые можно использовать.

  • @NoArgsConstructor: создает конструктор без аргументов.
  • @RequiredArgsConstructor: создает конструктор с обязательными параметрами, а атрибуты класса, аннотированные с помощью @NonNull, являются обязательными параметрами.
  • @AllArgsConstructor: создает конструктор со всеми аргументами.
/**
 * 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) {
        //无参构造器
        ConstructorExample example1 = new ConstructorExample();
        //全部参数构造器
        ConstructorExample example2 = new ConstructorExample(1L,"test",20);
        //@NonNull注解的必须参数构造器
        ConstructorExample example3 = ConstructorExample.of(1L);
    }
}

После компиляции Lombok сгенерирует следующий код.

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

    public ConstructorExample() {
    }

    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;
        }
    }
}

@Data

@Data — это удобная аннотация композиции, представляющая собой комбинацию @ToString, @EqualsAndHashCode, @Getter, @Setter и @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已生效
        DataExample example1 = new DataExample(1L);
        //@Getter @Setter已生效
        example1.setName("test");
        example1.setAge(20);
        //@ToString已生效
        System.out.println(example1);
        DataExample example2 = new DataExample(1L);
        //@EqualsAndHashCode已生效
        System.out.println(example1.equals(example2));
    }
}

После компиляции Lombok сгенерирует следующий код.

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() {
        return this.id;
    }

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

    public Integer getAge() {
        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 (!(o instanceof 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() {
        int PRIME = true;
        int result = 1;
        Object $id = this.getId();
        int result = result * 59 + ($id == null ? 43 : $id.hashCode());
        return result;
    }

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

@Value

С помощью аннотации @Value можно объявить класс неизменяемым, после объявления этот класс эквивалентен финальному классу и не может быть унаследован, а его свойства также станут финальными свойствами.

/**
 * 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) {
        //只能使用全参构造器
        ValueExample example = new ValueExample(1L,"test",20);
        // example.setName("andy") //没有生成setter方法,会报错
        // example.name="andy" //字段被设置为final类型,会报错
    }
}

После компиляции Lombok сгенерирует следующий код.

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() {
        return this.id;
    }

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

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

@Builder

С помощью аннотации @Builder можно создавать объекты в режиме построителя, а режим построителя добавляет цепочку вызовов, создавать объекты так удобно!

/**
 * 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);
    }
}

После компиляции Lombok сгенерирует следующий код.

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() {
        return new BuilderExample.BuilderExampleBuilder();
    }

    public String toString() {
        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() {
            return new BuilderExample(this.id, this.name, this.age);
        }

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

@SneakyThrows

Все еще ловите и выбрасываете исключения вручную? Попробуйте это автоматически, используя аннотацию @SneakyThrows!

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

    //自动抛出异常,无需处理
    @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);
    }
}

После компиляции Lombok сгенерирует следующий код.

public class SneakyThrowsExample {
    public SneakyThrowsExample() {
    }

    public static byte[] str2byte(String str) {
        try {
            return str.getBytes("UTF-8");
        } catch (UnsupportedEncodingException var2) {
            throw var2;
        }
    }
}

@Synchronized

Когда мы обращаемся к одному и тому же ресурсу в нескольких потоках, часто возникают проблемы с безопасностью потоков.В прошлом мы часто использовали ключевое слово synchronized для изменения методов для достижения синхронного доступа. Синхронизированный доступ также может быть достигнут с помощью аннотации @Synchronized.

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) {
        //添加@Synchronized三个线程可以同步调用reduceCount方法
        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() {
            while (example.getCount() > 0) {
                example.reduceCount(id);
            }
        }
    }
}

После компиляции Lombok сгенерирует следующий код.

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) {
            throw var7;
        }
    }
}

@With

С помощью аннотации @With можно клонировать исходный объект и изменить одно из его свойств, при этом необходимо указать полнопараметрический метод построения.

@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并设置age,返回false
        System.out.println(example1.equals(example2));
    }
}

После компиляции Lombok сгенерирует следующий код.

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;
    }
}

@Getter(lazy=true)

Когда мы получаем свойство, которое потребляет больше ресурсов, мы можем добавить @Getterlazy=trueСвойство реализует отложенную загрузку, и для отложенной загрузки свойства будет сгенерирован шаблонный код Double Check Lock.

/**
 * 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) {
        //使用Double Check Lock 样板代码对属性进行懒加载
        GetterLazyExample example = new GetterLazyExample();
        System.out.println(example.getCached().length);
    }
}

После компиляции Lombok сгенерирует следующий код.

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

    public GetterLazyExample() {
    }

    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));
    }
}

@Log

Используя аннотацию @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");
    }
}

После компиляции Lombok сгенерирует следующий код.

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

    public LogExample() {
    }

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

@Slf4j

При использовании Lombok для создания объектов журнала существует множество аннотаций, которые можно использовать в зависимости от реализации журнала. Например, @Log, @Log4j, @Log4j2, @Slf4j и т. д.

/**
 * 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");
    }
}

После компиляции Lombok сгенерирует следующий код.

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

    public LogSlf4jExample() {
    }

    public static void main(String[] args) {
        log.info("level:{}", "info");
        log.warn("level:{}", "warn");
        log.error("level:{}", "error");
    }
}

Принцип Ломбока

Если IDEA не установит подключаемый модуль Lombok, мы не сможем скомпилировать проект, использующий Lombok. После установки IDEA запросит у нас методы и свойства, которые Lombok генерирует для нас.

После использования аннотации @Data вы можете найти геттер, сеттер, toString и другие методы, просмотрев структуру класса.

Откройте целевой каталог.classфайл, мы можем увидеть код, сгенерированный Lombok для нас.Можно увидеть, что Lombok расширяет функциональность кода Java, анализируя аннотации, а затем генерируя код во время компиляции.

использованная литература

Официальная документация:проект Lombok.org/features/ali…

Адрес исходного кода проекта

GitHub.com/macro-positive/…

Эта статьяGitHubGitHub.com/macro-positive/…Записано, приветствую всех на Star!