在面向对象编程中必不可少需要在代码中定义对象模型,而在基于Java的业务平台开发实践中尤其如此。相信大家在平时开发中也深有感触,本来是没有多少代码开发量的,但是因为定义的业务模型对象比较多,而需要重复Getter/Setter、构造器方法、字符串输出的ToString方法和Equals/HashCode方法等。那么是否一款插件或工具能够替大家完成这些繁琐的操作呢?
public class Person {
private String name;
private int age;
public static class Builder {
private String name;
private int age;
public Builder() {
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Person build() {
return new Person(this.name, this.age);
}
}
private Person(String name, int age) {
this.name = name;
this.age = age;
}
}
@Builder
public class PersonUseLombok {
private String name;
private int age;
}
Project Lombok是一个可以自动插入你的编辑器和构建工具的java库。
打开IDEA的Setting –> 选择Plugins选项 –> 选择MarketPlace –> 搜索lombok –> 点击安装 –> 安装完成重启IDEA –> 安装成功
2. 离线安装
选择Install Plugin from Disk,安装从IDEA官网下载的离线包。
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.6version>
<scope>providedscope>
dependency>
dependencies>
lombok官方提供了delombok
工具,在Maven工程,在pom.xml
文件中加入lombok-maven-plugin
插件。生成的代码在target/generate-sources/delombok
目录下。
<plugin>
<groupId>org.projectlombokgroupId>
<artifactId>lombok-maven-pluginartifactId>
<version>1.18.6.0version>
<executions>
<execution>
<phase>generate-sourcesphase>
<goals>
<goal>delombokgoal>
goals>
<configuration>
<addOutputDirectory>falseaddOutputDirectory>
configuration>
execution>
executions>
plugin>
val
可以使用val作为本地变量的声明类型,来代替实际写入类型。执行此操作时,将会从初始化表达式推断出具体类型,局部变量也被声明为final
。
注意:此功能仅适用于局部变量和foreach
循环,而不适用于***成员变量***。
import lombok.val;
public class ExampleVal {
val name ="heankang";//报错,不能成员变量
private void example() {
val name ="heankang";//不报错
}
}
import java.util.ArrayList;
import java.util.HashMap;
import lombok.val;
public class ValExample {
public String example() {
//相当于final ArrayList example = new ArrayList();
val example = new ArrayList<String>();
example.add("Hello, World!");
//相当于 final String foo = example.get(0);
val foo = example.get(0);
return foo.toLowerCase();
}
public void example2() {
//相当于final HashMap map = new HashMap();
val map = new HashMap<Integer, String>();
map.put(0, "zero");
map.put(5, "five");
//相当于final Map.Entry entry : map.entrySet()
for (val entry : map.entrySet()){
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
}
}
var
除了局部变量没有标记为final
,var和val完全一样。
@Cleanup
该注解使用在属性前,该注解是用来保证分配的资源被释放。在本地变量上使用该注解,任何后续代码都将封装在try/finally
中,确保当前作用于中的资源被释放。默认@Cleanup
清理的方法为close()
,可以使用注解的value指定不同的无参方法名称@Cleanup(“xxxx”)
import lombok.Cleanup;
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream(args[0]);
try {
OutputStream out = new FileOutputStream(args[1]);
try {
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (in != null) {
in.close();
}
}
}
}
@Cleanup 不能调用有参清理函数,需要调用有参清理函数需要手工操作
@Cleanup 如果清理方法抛出异常,它将抢占抛出方法体内的任何异常。这可能导致问题的实际原因被掩埋,因此在选择使用Project Lombok的资源管理时应该考虑这个问题
**推荐使用try-with-resources**
import java.io.*;
public class CleanupByTryWithResources {
public static void main(String[] args) throws IOException {
try (InputStream in = new FileInputStream(args[0]);
OutputStream out = new FileOutputStream(args[1])) {
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}
}
@Getter/@Setter
该注解使用在类或者属性上,为字段生成Getter
和Setter
方法。
Getter
和Setter
方法getXxxxx
和setXxxx
,可以用is+大写开头的Boolean类型AccessLevel.NONE
禁用某个字段上的@Setter、@Getter
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class GetterSetterExample {
private int age = 10;
@Getter(AccessLevel.NONE)@Setter(AccessLevel.PROTECTED) private String name;
}
public class GetterSetterExample {
private int age = 10;
private String name;
//自动生成Getter、Setter
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//属性优先级高于类
protected void setName(String name) {
this.name = name;
}
}
@ToString
生成toString()
方法,默认情况下,它会以逗号分隔,按顺序,打印类名称以及每个非静态成员变量。
includeFieldNames
来控制输出中是否包含的属性名称。@ToString.Exclude
注解某一字段,可以从生成的方法中排除特定字段。@ToString(onlyExplicitlyIncluded = true)
标注类,然后使用@ToString.Include
标注想要输入的字段。@ToString(callSuper = true)
参数继承父类的输出。import lombok.ToString;
@ToString
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
@ToString.Exclude private int id;
public String getName() {
return this.name;
}
//继承父类,排除属性名
@ToString(callSuper=true, includeFieldNames=false)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
private static class Square extends Shape {
private final int width;
private final int height;
private Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public java.lang.String toString() {
//继承父类,排除属性名
return "ToStringExample.Square(super=" + super.toString() + ", " + this.width + ", " + this.height + ")";
}
}
@Override
public java.lang.String toString() {
//exclude "id"
return "ToStringExample(name=" + this.getName() + ", shape=" + this.shape + ", tags=" + java.util.Arrays.deepToString(this.tags) + ")";
}
}
@EqualsAndHashCode
任何类使用@EqualsAndHashCode标注生成hashCode()和equals()方法,默认情况下,它将使用所有非静态,非transient字段。但可以通过在可选的@EqualsAndHashCode.Include 或者@EqualsAndHashCode.Exclude注解字段来排除或包含指定字段
类似于@toString
@EqualsAndHashCode.Exclude
排除具体字段@EqualsAndHashCode.Include
包含指定字段,需和属性onlyExplicitlyIncluded = true
配合使用callSuper = true
继承父类。import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
@EqualsAndHashCode.Exclude private Shape shape = new Square(5, 10);
private String[] tags;
@EqualsAndHashCode.Exclude private int id;
public String getName() {
return this.name;
}
@EqualsAndHashCode(callSuper=true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
import java.util.Arrays;
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCodeExample)) return false;
EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (Double.compare(this.score, other.score) != 0) return false;
if (!Arrays.deepEquals(this.tags, other.tags)) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.score);
result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.tags);
return result;
}
protected boolean canEqual(Object other) {
return other instanceof EqualsAndHashCodeExample;
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Square)) return false;
Square other = (Square) o;
if (!other.canEqual((Object)this)) return false;
if (!super.equals(o)) return false;
if (this.width != other.width) return false;
if (this.height != other.height) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + super.hashCode();
result = (result*PRIME) + this.width;
result = (result*PRIME) + this.height;
return result;
}
protected boolean canEqual(Object other) {
return other instanceof Square;
}
}
}
@NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor
给类增加无参构造器、指定参数的构造器、包含所有参数的构造器
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.NonNull;
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
private int x, y;
private final String name;
@NonNull
private T description;
private static int count;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull
private String field;
}
}
// Generated by delombok at Mon Feb 25 09:40:52 CST 2019
package com.zybank.lombokdemo.entity;
import lombok.*;
public class ConstructorExample<T> {
private int x;
private int y;
private final String name;
@NonNull
private T description;
private static int count;
public static class NoArgsExample {
@NonNull
private String field;
@java.lang.SuppressWarnings("all")
public NoArgsExample() {
}
}
private ConstructorExample(final String name, @NonNull final T description) {
if (description == null) {
throw new java.lang.NullPointerException("description is marked @NonNull but is null");
}
this.name = name;
this.description = description;
}
public static <T> ConstructorExample<T> of(final String name, @NonNull final T description) {
return new ConstructorExample<T>(name, description);
}
protected ConstructorExample(final int x, final int y, final String name, @NonNull final T description) {
if (description == null) {
throw new java.lang.NullPointerException("description is marked @NonNull but is null");
}
this.x = x;
this.y = y;
this.name = name;
this.description = description;
}
}
@Data
@Data
包含了 @ToString
、@EqualsAndHashCode
、@Getter
/@Setter
和@RequiredArgsConstructor
的功能。
虽然@Data
注解非常有用,但是它的控制粒度较粗(Class级别),而且@Data
不能设置callSuper
, includeFieldNames
和exclude
等,如果需要改变默认值,可以使用相应的注解,@Data
会自动遵循这些注解。
@Data
提供了一个可以生成静态工厂的单一参数@Data(staticConstructor=”of”)
,会生成一个of()的静态工厂方法,并把构造方法设置为私有的。
package com.zybank.lombokdemo;
import lombok.*;
@ToString(includeFieldNames=false)
//生成静态工厂
@Data(staticConstructor = "of")
public class DataExample {
private final String name;
//自定义Setter方法
@Setter(AccessLevel.PACKAGE)
private int age;
private double score;
private String[] tags;
}
// Generated by delombok at Mon Feb 25 16:27:59 CST 2019
package com.zybank.lombokdemo;
import lombok.*;
public class DataExample {
private final String name;
private int age;
private double score;
private String[] tags;
@java.lang.Override
public java.lang.String toString() {
return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + java.util.Arrays.deepToString(this.getTags()) + ")";
}
private DataExample(final String name) {
this.name = name;
}
public static DataExample of(final String name) {
return new DataExample(name);
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
@java.lang.SuppressWarnings("all")
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
public void setScore(final double score) {
this.score = score;
}
void setAge(final int age) {
this.age = age;
}
public void setTags(final String[] tags) {
this.tags = tags;
}
@java.lang.Override
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof DataExample)) return false;
final DataExample other = (DataExample) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$name = this.getName();
final java.lang.Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
if (this.getAge() != other.getAge()) return false;
if (java.lang.Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!java.util.Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
protected boolean canEqual(final java.lang.Object other) {
return other instanceof DataExample;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
result = result * PRIME + this.getAge();
final long $score = java.lang.Double.doubleToLongBits(this.getScore());
result = result * PRIME + (int) ($score >>> 32 ^ $score);
result = result * PRIME + java.util.Arrays.deepHashCode(this.getTags());
return result;
}
}
@Value
该注解用于修饰类,是@Data
的不可变形式,生成immutable Class。字段都被修饰为private
和final
,默认的情况下不会生成settter
,默认类本身也是final
的。
实际上@Value
等价于final
@ToString
@EqualsAndHashCode
@AllArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@Getter
import lombok.AccessLevel;
import lombok.experimental.NonFinal;
import lombok.experimental.Wither;
import lombok.ToString;
import lombok.Value;
@Value public class ValueExample {
String name;
@Wither(AccessLevel.PACKAGE) @NonFinal int age;
double score;
protected String[] tags;
@ToString(includeFieldNames=true)
@Value(staticConstructor="of")
public static class Exercise<T> {
String name;
T value;
}
}
import java.util.Arrays;
public final class ValueExample {
private final String name;
private int age;
private final double score;
protected final String[] tags;
@java.beans.ConstructorProperties({"name", "age", "score", "tags"})
public ValueExample(String name, int age, double score, String[] tags) {
this.name = name;
this.age = age;
this.score = score;
this.tags = tags;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
@java.lang.Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ValueExample)) return false;
final ValueExample other = (ValueExample)o;
final Object this$name = this.getName();
final Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
result = result * PRIME + this.getAge();
final long $score = Double.doubleToLongBits(this.getScore());
result = result * PRIME + (int)($score >>> 32 ^ $score);
result = result * PRIME + Arrays.deepHashCode(this.getTags());
return result;
}
@java.lang.Override
public String toString() {
return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";
}
ValueExample withAge(int age) {
return this.age == age ? this : new ValueExample(name, age, score, tags);
}
public static final class Exercise<T> {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> Exercise<T> of(String name, T value) {
return new Exercise<T>(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
@java.lang.Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ValueExample.Exercise)) return false;
final Exercise<?> other = (Exercise<?>)o;
final Object this$name = this.getName();
final Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
final Object this$value = this.getValue();
final Object other$value = other.getValue();
if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
final Object $value = this.getValue();
result = result * PRIME + ($value == null ? 43 : $value.hashCode());
return result;
}
@java.lang.Override
public String toString() {
return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";
}
}
}
@NonNull
使用@NonNull
注解标注方法或构造函数的参数,让lombok
为您生成null-check
语句。
配合lomok
中其他生成完整方法和构造函数的注解(比如@Setter
注解),仅需将@NonNull
标注在成员变量上,就可在生成的方法和构造函数中加入null-check
。反之,就必须在入参中标注。
lombok
会探测开发者自己编写的null-check
语句,如果存在就不在生成。if
条件语句必须严格按照PARAMNAME == null
的形式书写。
import lombok.NonNull;
public class NonNullExample {
private String name;
public void setName(@NonNull String name) {
this.name = name;
}
}
public class NonNullExample {
private String name;
public void setName(String name) {
if (name == null) {
throw new NullPointerException("name is marked @NonNull but is null");
}
this.name = name;
}
}
@Builder
@Builder
注释为你的类生成复杂的构建器API,它把我们的Bean类包装为一个构建者模式,编译时增加了一个Builder内部类和全字段的构造器。
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
@Builder
public class BuilderExample {
@Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
@Singular private Set<String> occupations;
}
import java.util.Set;
public class BuilderExample {
private long created;
private String name;
private int age;
private Set<String> occupations;
BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
private static long $default$created() {
return System.currentTimeMillis();
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private long created;
private boolean created$set;
private String name;
private int age;
private java.util.ArrayList<String> occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder created(long created) {
this.created = created;
this.created$set = true;
return this;
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
Set<String> occupations = ...;
return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
}
@java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}
@SneakyThrows
该注解的作用是将检查异常包装为运行时异常,那么编码时就无需处理异常了。自动抛受检异常, 而无需显式在方法上使用throws
语句。把checked异常转化为unchecked异常,不再向上层方法抛出。
package com.zybank.lombokdemo.entity;
import lombok.SneakyThrows;
import java.io.UnsupportedEncodingException;
public class SneakyThrowsExample{
@SneakyThrows(UnsupportedEncodingException.class)
public String utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}
@SneakyThrows
public void run() {
throw new Throwable();
}
}
public class SneakyThrowsExample implements Runnable {
public String utf8ToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw Lombok.sneakyThrow(e);
}
}
public void run() {
try {
throw new Throwable();
} catch (Throwable t) {
throw Lombok.sneakyThrow(t);
}
}
}
@Synchronized
这个注解用在类方法(static修饰)或者实例方法上,效果和synchronized关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象,而@Synchronized得锁对象分别是私有静态final对象 L O C K 和 私 有 f i n a l 对 象 LOCK和私有final对象 LOCK和私有final对象lock(如果字段不存在,则会创建),当然,也可以自己指定锁对象
给方法加上同步锁,如果直接指定了value=xxx,其中xxx为类的一个成员,那么该方法使用该成员xxx作为加锁对象,放在同步块中执行
import lombok.Synchronized;
public class SynchronizedExample {
private final Object readLock = new Object();
@Synchronized
public static void hello() {
System.out.println("world");
}
@Synchronized
public int answerToLife() {
return 42;
}
@Synchronized("readLock")
public void foo() {
System.out.println("bar");
}
}
public class SynchronizedExample {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public static void hello() {
synchronized($LOCK) {
System.out.println("world");
}
}
public int answerToLife() {
synchronized($lock) {
return 42;
}
}
public void foo() {
synchronized(readLock) {
System.out.println("bar");
}
}
}
@Getter(lazy=true)
标注字段为懒加载字段,懒加载字段在创建对象时不会进行真正的初始化,而是在第一次访问的时候才会初始化,后面再次访问也不会重复初始化。
import lombok.Getter;
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;
}
}
delombok: Double-checked locking and the Singleton pattern
public class GetterLazyExample {
private final java.util.concurrent.AtomicReference<java.lang.Object> cached = new java.util.concurrent.AtomicReference<java.lang.Object>();
public double[] getCached() {
java.lang.Object value = this.cached.get();
if (value == null) {
synchronized(this.cached) {
value = this.cached.get();
if (value == null) {
final double[] actualValue = expensive();
value = actualValue == null ? this.cached : actualValue;
this.cached.set(value);
}
}
}
return (double[])(value == this.cached ? null : value);
}
private double[] expensive() {
double[] result = new double[1000000];
for (int i = 0; i < result.length; i++) {
result[i] = Math.asin(i);
}
return result;
}
}
@Log
@log
用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,具体如下:
@CommonsLog
Creates private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@Flogger
Creates private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
@JBossLog
Creates private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@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);
@XSlf4j
Creates private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
简单日记门面(simple logging Facade for java)SLF4J
是为各种loging APIs提供一个简单统一的接口,从而使得最终用户能够在部署的时候配置自己希望的loging APIs实现。
slf4j
为不同的日志框架,提供了适配工具方便与slf4j
门面连接:
同样,为存在多种日志框架的复杂大型项目中也提供了解决方法:
要了解Project Lombok的工作原理,首先必须了解Java编译的工作原理。 OpenJDK提供了编译过程的完美概述。换句话说,编译有3个阶段:
在Parse和Enter阶段,编译器将源文件解析为抽象语法树(AST)。将AST视为Java代码的DOM等价物。如果语法无效,解析只会抛出错误。在阶段3中检查诸如无效类或方法使用之类的编译错误。
在注释处理阶段,调用自定义注释处理器。这被认为是预编译阶段。注释处理器可以执行诸如验证类或生成新资源(包括源文件)之类的操作。注释处理器可能会生成错误,导致编译过程失败。如果由于注释处理而生成新的源文件,则编译将循环回Parse和Enter阶段,并重复该过程,直到不生成新的源文件。
在最后阶段,分析和生成,编译器从阶段1中生成的抽象语法树生成类文件(字节代码)。作为此过程的一部分,将分析AST是否存在损坏的引用(例如:未找到类,未找到方法)检查有效流(例如没有无法到达的语句),执行类型擦除,解除语法糖(例如,增强循环变为迭代器循环),最后,如果一切都成功,则写出Class文件。
Project Lombok正是依靠可插件化的Java自定义注解处理API(JSR 269: Pluggable Annotation Processing API)来实现在Javac编译阶段利用“Annotation Processor”对自定义的注解进行预处理后生成真正在JVM上面执行的“Class文件”。类似的还有AutoValue和Immutables。
从上面的Lombok执行的流程图中可以看出,在Javac解析成AST抽象语法树之后, Lombok 根据自己编写的注解处理器,动态地修改 AST,增加新的节点(即Lombok自定义注解所需要生成的代码),最终通过分析生成JVM可执行的字节码Class文件。如需要更加深入理解Project Lombok的技术细节,请阅读源码。
Lombok Project 是一款强大的工具,他提供一系列的注解去减少大量样版(Boilerplate)代码的编写。简单的一个注解,有时甚至能减少上百行代码。使Java类变得干净、整洁、易于维护。
Lombok Project一直以来也伴随着不少争议,Don’t use Lombok、Is it safe to use Project Lombok?中,进行了诸多讨论。主要有以下几方面问题:
Project Lombok
Project Lombok - Trick Explained
slf4j
Java开发神器Lombok的使用与原理
Reducing Boilerplate Code with Project Lombok