Адрес фактического центра электронной коммерции 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…
Адрес исходного кода проекта
Эта статьяGitHubGitHub.com/macro-positive/…Записано, приветствую всех на Star!