一、Lombok简介
Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法。
二、为何要使用Lombok
我们在开发过程中,通常都会定义大量的JavaBean,然后通过IDE去生成其属性的构造器、hashcode、toString、getter、setter、equals方法,当要增加属性或者对某个属性进行改变时,比如命名、类型等,都需要重新去生成上面提到的这些方法。这样重复的劳动没有任何意义,Lombok里面的注解可以轻松解决这些问题。
-
简化冗余的JavaBean代码,使得实体文件很简洁。
-
大大提高JavaBean中方法的执行效率,省去重复的步骤
这里简单说下Lombok实现的原理:主要是通过抽象语法树(AST),在编译处理后,匹配到有其注解的类,那么注解编译器就会自动去匹配项目中的注解对应到在Lombok语法树中的注解文件,并经过自动编译匹配来生成对应类中的getter或者setter方法,达到简化代码的目的。
利用此原理,也可自行编写一些工作中一些经常使用到的,比如实体类转Map对象,map对象转实体类,原本使用Beanutils或者cglib的BeanCopier实现转换,前者使用的是反射的机制,所以性能相对较差,后者是使用修改字节码技术,性能在未使用Converter时基本等同于set和get方法。但说白了还是麻烦,毕竟还需要缓存对象等做到复用等。而使用Lombok的形式的话,一切都是自动的,性能基本是没有损失的。
三、IDEA中安装Lombok插件
1、在顶部工具栏中找到File → Settings
2、找到Plugins,点击 “Browse repositories”按钮
3、在搜索框输入Lombok,选择Lombok插件,点击 “Install”按钮进行安装,然后点击 “Close”按钮,最后重启IDEA即可生效
四、添加Lombok依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
五、Lombok的常用注解
-
@Getter/@Setter
-
@ToString
-
@NonNull
-
@NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor
-
@EqualsAndHashCode
-
@Data/@Value
-
@Cleanup
-
@Budilder
-
@SneakyThrows
-
@Synchronized
-
@Log
六、Lombok注解的介绍与使用
1、@Getter/@Setter
该注解使用在类或者成员变量上,如果使用在类上,可以为类中所有非静态成员变量生成对应的get 和 set方法。如果使用在成员变量上,只能为该成员变量生成对应的get 和 set方法。同时还能够为生成的get 和 set方法指定访问修饰符,如果不指定默认为public
1.1、使用在类上(不指定访问修饰符,默认为public)
package com.lombok;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class User {
private String username;
private String password;
}
实际上的效果相当于:
package com.lombok;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
1.2、使用在成员变量上,同时指定访问修饰符
package com.lombok;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
public class User {
@Getter
@Setter(AccessLevel.PROTECTED)
private String username;
@Getter(AccessLevel.PUBLIC)
@Setter(AccessLevel.PRIVATE)
private String password;
}
实际上的效果相当于:
package com.lombok;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
protected void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
private void setPassword(String password) {
this.password = password;
}
}
2、@ToString
该注解使用在类上,用来为类生成toString()方法,默认包含所有成员变量,同时可以使用exclude 或者 of用来从生成的toString()方法中排出 或者 包含某些指定的成员变量
2.1、使用eclude来排除指定的成员变量
package com.lombok;
import lombok.ToString;
@ToString(exclude = {"username","password"})
public class User {
private String username;
private String password;
private Integer age;
}
实际上的效果相当于:
package com.lombok;
public class User {
private String username;
private String password;
private Integer age;
@Override
public String toString() {
return "User(age=" + this.age + ")";
}
}
2.2、使用of来包含指定的成员变量
package com.lombok;
import lombok.ToString;
@ToString(of = {"username","password"})
public class User {
private String username;
private String password;
private Integer age;
}
实际上的效果相当于:
package com.lombok;
public class User {
private String username;
private String password;
private Integer age;
@Override
public String toString() {
return "User(username=" + this.username + ", password=" + this.password + ")";
}
}
3、@NonNull
该注解使用在成员变量上 或者 方法(包括:构造方法)的参数前面,会自动产生一个关于成员变量 或者 参数的非空检查,如果参数为空,则抛出一个空指针异常
package com.lombok;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
public class User {
@Getter
@Setter
@NonNull
private String username;
}
实际上的效果相当于:
package com.lombok;
import lombok.NonNull;
public class User {
@NonNull
private String username;
public void setUsername(@NonNull String username) {
if (username == null){
throw new NullPointerException("username");
}else{
this.username = username;
}
}
@NonNull
public String getUsername() {
return username;
}
}
4、@NoArgsConstructor/@RequiredArgsConstructor /@AllArgsConstructor
这三个注解都是用在类上
@NoArgsConstructor:生成一个无参的构造方法
@RequiredArgsConstructor:使用类中所有带有@NonNull注解的 或者 带有final修饰的成员变量生成对应的构造方法
@AllArgsConstructor:生成一个包含所以参数的构造方法
package com.lombok;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
public class User {
@NonNull
private String username;
private String password;
private Integer age;
}
实际上的效果相当于:
package com.lombok;
import lombok.NonNull;
public class User {
@NonNull
private String username;
private String password;
private Integer age;
public User() {
}
public User(String username, String password, Integer age) {
this.username = username;
this.password = password;
this.age = age;
}
public User(String username) {
this.username = username;
}
}
5、@EqualsAndHashCode
该注解使用在类上,为类生成equals、hashCode和canEqual方法 (用于判断某个对象是否是当前类的实例),生成方法时只会使用类中的非静态和非transient成员变量,同时@EqualsAndHashCode 也可以使用exclude 或者 of用来排除 或者 包含某些指定的字段做比较
package com.lombok;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class User {
private String username;
private String password;
private static Integer age;
}
实际上的效果相当于:
package com.lombok;
public class User {
private String username;
private String password;
private static Integer age;
@Override
public boolean equals(final Object o) {
if (o == this){
return true;
}
if (!(o instanceof User)) {
return false;
}
final User other = (User) o;
if (!other.canEqual((Object) this)) {
return false;
}
final Object this$username = this.username;
final Object other$username = other.username;
if (this$username == null ? other$username != null : !this$username.equals(other$username)) {
return false;
}
final Object this$password = this.password;
final Object other$password = other.password;
if (this$password == null ? other$password != null : !this$password.equals(other$password)) {
return false;
}
return true;
}
protected boolean canEqual(final Object other) {
return other instanceof User;
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $username = this.username;
result = result * PRIME + ($username == null ? 43 : $username.hashCode());
final Object $password = this.password;
result = result * PRIME + ($password == null ? 43 : $password.hashCode());
return result;
}
}
6、@Data/@Value
@Data注解相当于@Getter/@Setter、@ToString、@EqualsAndHashCode和@RequiredArgsConstructor的综合
@Value注解与@Data类似,唯一的区别在于它会把所有成员变量默认定义为private final修饰,并且不会生成set方法。
7、@Cleanup
这个注解用在变量前面,可以保证此变量代表的资源会被自动关闭,默认是调用资源的close()方法,如果该资源有其它关闭方法,可使用@Cleanup(“methodName”)来指定要调用的方法
package com.lombok;
import lombok.Cleanup;
import java.io.*;
public class MyTest {
public static void main(String[] args) throws IOException {
@Cleanup InputStream input = new FileInputStream(args[0]);
@Cleanup OutputStream output = new FileOutputStream(args[1]);
byte[] bytes = new byte[1024];
while (true) {
int num = input.read(bytes);
if (num == -1) {
break;
}
output.write(bytes, 0, num);
}
}
}
实际上的效果相当于:
package com.lombok;
import java.io.*;
public class MyTest {
public static void main(String[] args) throws IOException {
InputStream input = new FileInputStream(args[0]);
try {
OutputStream output = new FileOutputStream(args[1]);
try {
byte[] bytes = new byte[1024];
while (true) {
int readNum = input.read(bytes);
if (readNum == -1) {
break;
}
output.write(bytes, 0, readNum);
}
} finally {
if (output != null) {
output.close();
}
}
} finally {
if (input != null) {
input.close();
}
}
}
}
8、@Budilder
该注解使用在类上,可以使用Builder方式进行初始化,Builder的作用之一是为了解决在某个类中有很多构造函数的情况,同时也省去写很多构造函数的麻烦,在设计模式中的思想是:用一个内部类去实例化一个对象,避免一个类出现过多构造函数。
package com.lombok;
import lombok.Builder;
@Builder
public class User {
private String username;
private String password;
private Integer age;
}
实际上的效果相当于:
package com.lombok;
public class User {
private String username;
private String password;
private Integer age;
User(String username, String password, Integer age) {
this.username = username;
this.password = password;
this.age = age;
}
public static UserBuilder builder() {
return new UserBuilder();
}
public static class UserBuilder {
private String username;
private String password;
private Integer age;
UserBuilder() {
}
public UserBuilder username(String username) {
this.username = username;
return this;
}
public UserBuilder password(String password) {
this.password = password;
return this;
}
public UserBuilder age(Integer age) {
this.age = age;
return this;
}
public User build() {
return new User(username, password, age);
}
@Override
public String toString() {
return "User.UserBuilder(username=" + this.username + ", password=" + this.password + ", age=" + this.age + ")";
}
}
}
使用Builder方式对User类进行初始化:
package com.lombok;
public class MyTest {
public static void main(String[] args) {
User user = User.builder().username("lemon").password("123456").age(18).build();
}
}
9、@SneakyThrows
这个注解用在方法上,可以将方法中的代码用try-catch语句包裹起来,捕获异常并在catch中用Lombok.sneakyThrow(e)把异常抛出,可以使用@SneakyThrows(Exception.class)的形式指定抛出哪种异常
package com.lombok;
import lombok.SneakyThrows;
import java.io.UnsupportedEncodingException;
public class SneakyThrowsExample implements Runnable {
@SneakyThrows(UnsupportedEncodingException.class)
public String utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}
@SneakyThrows
public void run() {
throw new Throwable();
}
}
实际上的效果相当于:
package com.lombok;
import lombok.Lombok;
import java.io.UnsupportedEncodingException;
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);
}
}
}
10、@Synchronized
这个注解用在类方法或者实例方法上,效果和synchronized关键字相同,区别在于锁对象不同,对于类方法和实例方法,synchronized关键字的锁对象分别是类的class对象和this对象,这可能会导致死锁现象。一般情况下建议锁定一个专门用于此目的的独立锁,而不是允许公共对象进行锁定。 而@Synchronized的锁对象分别是私有静态final对象l锁和私有final对象锁。
package com.lombok;
import lombok.Synchronized;
public class MyTest {
private final Object lock = new Object();
@Synchronized
public static void show() {
System.out.println("Hello World");
}
@Synchronized(value = "lock")
public void good() {
System.out.println("Today is a good day");
}
}
实际上的效果相当于:
package com.lombok;
public class MyTest {
private static final Object $LOCK = new Object[0];
private final Object lock = new Object();
public static void show() {
synchronized ($LOCK) {
System.out.println("Hello World");
}
}
public void good() {
synchronized (lock) {
System.out.println("Today is a good day");
}
}
}
11、@Log
这个注解用在类上,可以省去从日志工厂生成日志对象这一步,直接进行日志记录,具体注解根据日志工具的不同而不同,同时,可以在注解中可以使用topic来指定生成log对象时的类名。默认情况下,将使用@Log注解的类的类名称作为生成log对象时的类名
package com.lombok;
import lombok.extern.apachecommons.CommonsLog;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
@Log
public class LogExample {
public static void main(String... args) {
System.out.println("Something's wrong here");
}
}
@Slf4j
class LogExampleOther {
public static void main(String... args) {
System.out.println("Something else is wrong here");
}
}
@CommonsLog(topic="CounterLog")
class LogExampleCategory {
public static void main(String... args) {
System.out.println("Calling the 'CounterLog' with a message");
}
}
实际上的效果相当于:
package com.lombok;
public class LogExample {
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
public static void main(String... args) {
System.out.println("Something's wrong here");
}
}
class LogExampleOther {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);
public static void main(String... args) {
System.out.println("Something else is wrong here");
}
}
class LogExampleCategory {
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");
public static void main(String... args) {
System.out.println("Calling the 'CounterLog' with a message");
}
}
@Log不同日志注解的总结,如下所示
@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);