我们在开发过程中,通常都会定义大量的JavaBean,然后通过IDE去生成其属性的构造器、getter、setter、equals、hashcode、toString方法,当要对某个属性进行改变时,比如命名、类型等,都需要重新去生成上面提到的这些方法,而且这些方法对业务逻辑,基本上不会有任何的作用。Lombok就是用来简化java的代码开发而生的,通过集成到IDE中,Lombok能够注入开发人员的代码并立即生效。例如,只需将@Data注释添加到数据类中,如下所示,会导致IDE中的一些新方法
github地址:https://github.com/rzwitserloot/lombok
从上图中,我们可以发现,我们就在类上加了一个@Data注解,视图中就自动为我们生成了getter,setter,toString,hashcode,equals以及无参的构造方法。@Data的功能也正式如此。那么怎么来使用Lombok了?
下载地址:https://projectlombok.org/download.html
java -jar lombok.jar
安装程序将尝试检测支持的IDE的位置。 如果无法正确确定安装IDE的位置,则可以手动指定位置。只需点击“安装/更新”,IDE集成即可完成。
注意:安装完后需要退出/重启,本人测试直接通过restart IDE,发现无效。
在pom文件中加入依赖即可:
org.projectlombok
lombok
provided
可以使用val作为局部变量声明的类型,Lombok将从初始化程序表达式推断该类型。此功能仅适用于局部变量和foreach循环,而不适用于字段。注意:使用val时,该局部变量必须是已经初始化了的。
import java.util.ArrayList;
import java.util.HashMap;
import lombok.val;
public class ValExample {
public String example() {
val example = new ArrayList();
example.add("Hello, World!");
val foo = example.get(0);
return foo.toLowerCase();
}
public void example2() {
val map = new HashMap();
map.put(0, "zero");
map.put(5, "five");
for (val entry : map.entrySet()) {
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
}
}
上面代码对应的java代码如下:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class ValExample {
public String example() {
final ArrayList example = new ArrayList();
example.add("Hello, World!");
final String foo = example.get(0);
return foo.toLowerCase();
}
public void example2() {
final HashMap map = new HashMap();
map.put(0, "zero");
map.put(5, "five");
for (final Map.Entry entry : map.entrySet()) {
System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
}
}
}
注:此功能目前在NetBeans中暂时不起作用
Lombok annotated code:
@Getter的懒加载:懒加载是一种美德!
import lombok.Getter;
public class GetterLazyExampleBefore {
@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;
}
}
对应的java代码如下:
public class GetterLazyExample {
private final java.util.concurrent.atomic.AtomicReference cached = new java.util.concurrent.atomic.AtomicReference();
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;
}
}
建议不要使用懒加载,像这种比较复杂的代码生成,还是自己写!
NullPointerException
异常。如果该属性在构造方法当中,那么构造方法默认会抛出NullPointerException
异常
用法:
@Getter @Setter @NonNull
private List members;
生成的java代码如下:
@NonNull
private List members;
public Family(@NonNull final List members) {
if (members == null) throw new java.lang.NullPointerException("members");
this.members = members;
}
@NonNull
public List getMembers() {
return members;
}
public void setMembers(@NonNull final List members) {
if (members == null) throw new java.lang.NullPointerException("members");
this.members = members;
}
测试代码如下:
@NoArgsConstructor
@AllArgsConstructor
public class Person2 {
@Setter @Getter @NonNull private String name;
@Setter @Getter private String ssn;
@Setter @Getter private String address;
@Setter @Getter private String city;
@Setter @Getter private String state;
@Setter @Getter private String zip;
}
public class Person2Test {
@Test
public void test(){
Person2 p = new Person2(null, null, null, null, null, null);
System.out.println(p);
}
}
测试结果会抛NullPointerException异常!
@Entity
@Table(name="t_user")
@NoArgsConstructor
@AllArgsConstructor
@ToString(includeFieldNames=false)// 如果值为false,则不会显示属性名称,如果值为true,则会显示属性名称
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
@Id()
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Setter @Getter private String name;
@Setter @Getter private String address;
@Setter @Getter private int age;
@Setter @Getter private boolean isSuccess;
}
测试代码如下:
public class UserTest {
@Test
public void test(){
User user = new User(1, "chhliu", "北京", 25, true);
System.out.println(user);
}
}
测试结果如下:
User(1, chhliu, 北京, 25, true)
如果将上面的false改为true,则测试结果如下
User(id=1, name=chhliu, address=北京, age=25, isSuccess=true)
@NoArgsConstructor
@AllArgsConstructor
@ToString(includeFieldNames=true, exclude={"name", "address"})
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
@Id()
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Setter @Getter private String name;
@Setter @Getter private String address;
@Setter @Getter private int age;
@Setter @Getter private boolean isSuccess;
}
测试结果如下:
User(id=1, age=25, isSuccess=true)
exclude用来排除不需要显示的属性。
@NoArgsConstructor
@AllArgsConstructor
@ToString(includeFieldNames=true, of={"name"})
public class User implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
@Id()
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
@Setter @Getter private String name;
@Setter @Getter private String address;
@Setter @Getter private int age;
@Setter @Getter private boolean isSuccess;
}
测试结果如下:
User(name=chhliu)
of的作用与exclude的作用相反,包含需要显示的属性。
@ToString(callSuper=true,exclude="someExcludedField")
public class Foo extends Bar {
private boolean someBoolean = true;
private String someStringField;
private float someExcludedField;
}
对应生成的java代码如下:
public class Foo extends Bar {
private boolean someBoolean = true;
private String someStringField;
private float someExcludedField;
@java.lang.Override
public java.lang.String toString() {
return "Foo(super=" + super.toString() +
", someBoolean=" + someBoolean +
", someStringField=" + someStringField + ")";
}
}
会将父类中的属性也显示出来。
@EqualsAndHashCode(callSuper=true,exclude={"address","city","state","zip"})
public class Person extends SentientBeing {
enum Gender { Male, Female }
@NonNull private String name;
@NonNull private Gender gender;
private String ssn;
private String address;
private String city;
private String state;
private String zip;
}
生成的java代码如下:
public class Person extends SentientBeing {
enum Gender {
/*public static final*/ Male /* = new Gender() */,
/*public static final*/ Female /* = new Gender() */;
}
@NonNull
private String name;
@NonNull
private Gender gender;
private String ssn;
private String address;
private String city;
private String state;
private String zip;
@java.lang.Override
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != this.getClass()) return false;
if (!super.equals(o)) return false;
final Person other = (Person)o;
if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;
if (this.ssn == null ? other.ssn != null : !this.ssn.equals(other.ssn)) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = result * PRIME + super.hashCode();
result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
result = result * PRIME + (this.gender == null ? 0 : this.gender.hashCode());
result = result * PRIME + (this.ssn == null ? 0 : this.ssn.hashCode());
return result;
}
}
该注解的使用方法和@toString的使用方法类似。
@Data注释可能是Project Lombok工具集中最常用的注释。它结合了@ToString,@EqualsAndHashCode,@Getter和@Setter的功能。
@Data(staticConstructor="of")
public class Company {
private final Person founder;
private String name;
private List employees;
}
生成的java代码如下:
public class Company {
private final Person founder;
private String name;
private List employees;
private Company(final Person founder) {
this.founder = founder;
}
public static Company of(final Person founder) {
return new Company(founder);
}
public Person getFounder() {
return founder;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public List getEmployees() {
return employees;
}
public void setEmployees(final List employees) {
this.employees = employees;
}
@java.lang.Override
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (o == null) return false;
if (o.getClass() != this.getClass()) return false;
final Company other = (Company)o;
if (this.founder == null ? other.founder != null : !this.founder.equals(other.founder)) return false;
if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
if (this.employees == null ? other.employees != null : !this.employees.equals(other.employees)) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = result * PRIME + (this.founder == null ? 0 : this.founder.hashCode());
result = result * PRIME + (this.name == null ? 0 : this.name.hashCode());
result = result * PRIME + (this.employees == null ? 0 : this.employees.hashCode());
return result;
}
@java.lang.Override
public java.lang.String toString() {
return "Company(founder=" + founder + ", name=" + name + ", employees=" + employees + ")";
}
}
下面我们来验证下这个注解的功能,先覆写资源的释放方法:
public void testCleanup(){
try {
@Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream() { // 覆写释放资源方法
@Override
public void close() throws IOException {
super.close();
System.out.println("I've been closed!");
}
};
baos.write(new byte[] {'Y','e','s'});
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
测试结果如下:
Yes
I've been closed!
通过上面的测试结果,可以得出,默认调用了close方法来释放资源。
public void testCleanUp() {
try {
@Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(new byte[] {'Y','e','s'});
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
生成的java代码如下:
public void testCleanUp() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
baos.write(new byte[]{'Y', 'e', 's'});
System.out.println(baos.toString());
} finally {
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void testSynchronized() {
Runnable runner = new Runnable() {
public void run() {
synchronized($lock) {
System.out.println("Thread locked on $lock and sleeping for 5 seconds. You should see the date output after the wait.");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {}
System.out.println("Done sleeping.");
}
}
};
new Thread(runner).start();
// Make sure we don't outrun the new thread.
try {Thread.sleep(500);} catch (InterruptedException e) {}
Date today = new Date();
System.out.println("Main thread attempting to lock on $lock.");
System.out.println(synchronizedFormat(today));
}
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
@Synchronized // 使用在方法上
public String synchronizedFormat(Date date) {
return format.format(date);
}
// 上面的代码会生成如下的java代码
private final java.lang.Object $lock = new java.lang.Object[0];
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
public String synchronizedFormat(Date date) {
synchronized ($lock) {
return format.format(date);
}
}
该注解可能会造成一些线程问题,建议慎用!
@NoArgsConstructor注解用来生成无参的构造函数
@AllArgsConstructor注解用来生成带参的构造函数(注意:生成的带参构造函数是带有所有的属性)
目前支持的日志框架如下:
下面以slf4j为例来进行说明
1、pom文件添加依赖
ch.qos.logback
logback-classic
1.1.2
2、在src/main/resources目录加入slf4j的配置文件
%date [%thread] %-5level %logger{80} - %msg%n
UTF-8
${logbase}%d{yyyy-MM-dd}.log.html
${logbase}.%d{yyyy-MM-dd}.log.html.zip
2MB
%date%level%thread%10logger%file%line%msg
3、测试类
package com.chhliu.lombok.lombokstart;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LogExampleOther {
public static void main(String... args) {
log.error("i'm the king of the world");
}
}
测试结果如下:
2017-05-15 21:15:31,262 [main] ERROR com.chhliu.lombok.lombokstart.LogExampleOther - i'm the king of the world
从上面的示例中,可以发现,@Log的功能还是非常方便的。Lombok还提供了一个简易的日志浏览界面,效果如下:
@Builder
public class BuilderExampleBefore {
private String name;
private int age;
}
public class BuilderExampleBeforeTest {
@Test
public void test(){
BuilderExampleBuilder builder = BuilderExample.builder().age(20).name("chhliu");// 链式api的方式
System.out.println(builder.toString());
}
}
从上面的示例中可以看到,我们使用了链式api来实现赋值,比构造方法一个个的set要好看许多吧!
Lombok到目前为止,存在很多的争议,具体技术选型的时候,要看项目的侧重点!
官网地址如下:http://jnb.ociweb.com/jnb/jnbJan2010.html