Lombok有啥牛皮的?SpringBoot和IDEA官方都要支持它!

文章推薦指數: 80 %
投票人數:10人

之后在项目的pom.xml文件中添加Lombok依赖,SpringBoot 2.1.x版本后无需指定Lombok版本,SpringBoot在 spring-boot-dependencies 中已经内置。

org.projectlombok lombok true Lombok使用Lombok中有很多注解,这些注解使得我们可以更加方便的编写Java代码,下面介绍下这些注解的使用。

val使用val注解可以取代任意类型作为局部变量,这样我们就不用写复杂的ArrayList和Map.Entry类型了,具体例子如下。

/** *Createdbymacroon2020/12/16. */ publicclassValExample{ publicstaticvoidexample(){ //val代替ArrayList和String类型 valexample=newArrayList(); example.add("HelloWorld!"); valfoo=example.get(0); System.out.println(foo.toLowerCase()); } publicstaticvoidexample2(){ //val代替Map.Entry类型 valmap=newHashMap(); map.put(0,"zero"); map.put(5,"five"); for(valentry:map.entrySet()){ System.out.printf("%d:%s\n",entry.getKey(),entry.getValue()); } } publicstaticvoidmain(String[]args){ example(); example2(); } }当我们使用了val注解后,Lombok会从局部变量的初始化表达式推断出具体类型,编译后会生成如下代码。

publicclassValExample{ publicValExample(){ } publicstaticvoidexample(){ ArrayListexample=newArrayList(); example.add("HelloWorld!"); Stringfoo=(String)example.get(0); System.out.println(foo.toLowerCase()); } publicstaticvoidexample2(){ HashMapmap=newHashMap(); map.put(0,"zero"); map.put(5,"five"); Iteratorvar1=map.entrySet().iterator(); while(var1.hasNext()){ Entryentry=(Entry)var1.next(); System.out.printf("%d:%s\n",entry.getKey(),entry.getValue()); } } }@NonNull在方法上使用@NonNull注解可以做非空判断,如果传入空值的话会直接抛出NullPointerException。

/** *Createdbymacroon2020/12/16. */ publicclassNonNullExample{ privateStringname; publicNonNullExample(@NonNullStringname){ this.name=name; } publicstaticvoidmain(String[]args){ newNonNullExample("test"); //会抛出NullPointerException newNonNullExample(null); } }编译后会在构造器中添加非空判断,具体代码如下。

publicclassNonNullExample{ privateStringname; publicNonNullExample(@NonNullStringname){ if(name==null){ thrownewNullPointerException("nameismarkednon-nullbutisnull"); }else{ this.name=name; } } publicstaticvoidmain(String[]args){ newNonNullExample("test"); newNonNullExample((String)null); } }@Cleanup当我们在Java中使用资源时,不可避免地需要在使用后关闭资源。

使用@Cleanup注解可以自动关闭资源。

/** *Createdbymacroon2020/12/16. */ publicclassCleanupExample{ publicstaticvoidmain(String[]args)throwsIOException{ StringinStr="HelloWorld!"; //使用输入输出流自动关闭,无需编写trycatch和调用close()方法 @CleanupByteArrayInputStreamin=newByteArrayInputStream(inStr.getBytes("UTF-8")); @CleanupByteArrayOutputStreamout=newByteArrayOutputStream(); byte[]b=newbyte[1024]; while(true){ intr=in.read(b); if(r==-1)break; out.write(b,0,r); } StringoutStr=out.toString("UTF-8"); System.out.println(outStr); } }编译后Lombok会生成如下代码。

publicclassCleanupExample{ publicCleanupExample(){ } publicstaticvoidmain(String[]args)throwsIOException{ StringinStr="HelloWorld!"; ByteArrayInputStreamin=newByteArrayInputStream(inStr.getBytes("UTF-8")); try{ ByteArrayOutputStreamout=newByteArrayOutputStream(); try{ byte[]b=newbyte[1024]; while(true){ intr=in.read(b); if(r==-1){ StringoutStr=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注解,我们再也不用编写getter/setter方法了。

试想下之前即使我们使用IDEA自动生成getter/setter方法,如果类属性的类型和名称改了,又要重新生成getter/setter方法也是一件很麻烦的事情。

/** *Createdbymacroon2020/12/17. */ publicclassGetterSetterExample{ @Getter @Setter privateStringname; @Getter @Setter(AccessLevel.PROTECTED) privateIntegerage; publicstaticvoidmain(String[]args){ GetterSetterExampleexample=newGetterSetterExample(); example.setName("test"); example.setAge(20); System.out.printf("name:%sage:%d",example.getName(),example.getAge()); } }编译后Lombok会生成如下代码。

publicclassGetterSetterExample{ privateStringname; privateIntegerage; publicGetterSetterExample(){ } publicStringgetName(){ returnthis.name; } publicvoidsetName(finalStringname){ this.name=name; } publicIntegergetAge(){ returnthis.age; } protectedvoidsetAge(finalIntegerage){ this.age=age; } }@ToString把所有类属性都编写到toString方法中方便打印日志,是一件多么枯燥无味的事情。

使用@ToString注解可以自动生成toString方法,默认会包含所有类属性,使用@ToString.Exclude注解可以排除属性的生成。

/** *Createdbymacroon2020/12/17. */ @ToString publicclassToStringExample{ @ToString.Exclude privateLongid; privateStringname; privateIntegerage; publicToStringExample(Longid,Stringname,Integerage){ this.id=id; this.name=name; this.age=age; } publicstaticvoidmain(String[]args){ ToStringExampleexample=newToStringExample(1L,"test",20); //自动实现toString方法,输出ToStringExample(name=test,age=20) System.out.println(example); } }编译后Lombok会生成如下代码。

publicclassToStringExample{ privateLongid; privateStringname; privateIntegerage; publicToStringExample(Longid,Stringname,Integerage){ this.id=id; this.name=name; this.age=age; } publicStringtoString(){ return"ToStringExample(name="+this.name+",age="+this.age+")"; } }@EqualsAndHashCode使用@EqualsAndHashCode注解可以自动生成hashCode和equals方法,默认包含所有类属性,使用@EqualsAndHashCode.Exclude可以排除属性的生成。

/** *Createdbymacroon2020/12/17. */ @Getter @Setter @EqualsAndHashCode publicclassEqualsAndHashCodeExample{ privateLongid; @EqualsAndHashCode.Exclude privateStringname; @EqualsAndHashCode.Exclude privateIntegerage; publicstaticvoidmain(String[]args){ EqualsAndHashCodeExampleexample1=newEqualsAndHashCodeExample(); example1.setId(1L); example1.setName("test"); example1.setAge(20); EqualsAndHashCodeExampleexample2=newEqualsAndHashCodeExample(); example2.setId(1L); //equals方法只对比id,返回true System.out.println(example1.equals(example2)); } }编译后Lombok会生成如下代码。

publicclassEqualsAndHashCodeExample{ privateLongid; privateStringname; privateIntegerage; publicEqualsAndHashCodeExample(){ } publicbooleanequals(finalObjecto){ if(o==this){ returntrue; }elseif(!(oinstanceofEqualsAndHashCodeExample)){ returnfalse; }else{ EqualsAndHashCodeExampleother=(EqualsAndHashCodeExample)o; if(!other.canEqual(this)){ returnfalse; }else{ Objectthis$id=this.getId(); Objectother$id=other.getId(); if(this$id==null){ if(other$id!=null){ returnfalse; } }elseif(!this$id.equals(other$id)){ returnfalse; } returntrue; } } } protectedbooleancanEqual(finalObjectother){ returnotherinstanceofEqualsAndHashCodeExample; } publicinthashCode(){ intPRIME=true; intresult=1; Object$id=this.getId(); intresult=result*59+($id==null?43:$id.hashCode()); returnresult; } }@XxConstructor使用@XxConstructor注解可以自动生成构造方法,有@NoArgsConstructor、@RequiredArgsConstructor和@AllArgsConstructor三个注解可以使用。

@NoArgsConstructor:生成无参构造函数。

@RequiredArgsConstructor:生成包含必须参数的构造函数,使用@NonNull注解的类属性为必须参数。

@AllArgsConstructor:生成包含所有参数的构造函数。

/** *Createdbymacroon2020/12/17. */ @NoArgsConstructor @RequiredArgsConstructor(staticName="of") @AllArgsConstructor publicclassConstructorExample{ @NonNull privateLongid; privateStringname; privateIntegerage; publicstaticvoidmain(String[]args){ //无参构造器 ConstructorExampleexample1=newConstructorExample(); //全部参数构造器 ConstructorExampleexample2=newConstructorExample(1L,"test",20); //@NonNull注解的必须参数构造器 ConstructorExampleexample3=ConstructorExample.of(1L); } }编译后Lombok会生成如下代码。

publicclassConstructorExample{ @NonNull privateLongid; privateStringname; privateIntegerage; publicConstructorExample(){ } privateConstructorExample(@NonNullfinalLongid){ if(id==null){ thrownewNullPointerException("idismarkednon-nullbutisnull"); }else{ this.id=id; } } publicstaticConstructorExampleof(@NonNullfinalLongid){ returnnewConstructorExample(id); } publicConstructorExample(@NonNullfinalLongid,finalStringname,finalIntegerage){ if(id==null){ thrownewNullPointerException("idismarkednon-nullbutisnull"); }else{ this.id=id; this.name=name; this.age=age; } } }@Data@Data是一个方便使用的组合注解,是@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor的组合体。

/** *Createdbymacroon2020/12/17. */ @Data publicclassDataExample{ @NonNull privateLongid; @EqualsAndHashCode.Exclude privateStringname; @EqualsAndHashCode.Exclude privateIntegerage; publicstaticvoidmain(String[]args){ //@RequiredArgsConstructor已生效 DataExampleexample1=newDataExample(1L); //@Getter@Setter已生效 example1.setName("test"); example1.setAge(20); //@ToString已生效 System.out.println(example1); DataExampleexample2=newDataExample(1L); //@EqualsAndHashCode已生效 System.out.println(example1.equals(example2)); } }编译后Lombok会生成如下代码。

publicclassDataExample{ @NonNull privateLongid; privateStringname; privateIntegerage; publicDataExample(@NonNullfinalLongid){ if(id==null){ thrownewNullPointerException("idismarkednon-nullbutisnull"); }else{ this.id=id; } } @NonNull publicLonggetId(){ returnthis.id; } publicStringgetName(){ returnthis.name; } publicIntegergetAge(){ returnthis.age; } publicvoidsetId(@NonNullfinalLongid){ if(id==null){ thrownewNullPointerException("idismarkednon-nullbutisnull"); }else{ this.id=id; } } publicvoidsetName(finalStringname){ this.name=name; } publicvoidsetAge(finalIntegerage){ this.age=age; } publicbooleanequals(finalObjecto){ if(o==this){ returntrue; }elseif(!(oinstanceofDataExample)){ returnfalse; }else{ DataExampleother=(DataExample)o; if(!other.canEqual(this)){ returnfalse; }else{ Objectthis$id=this.getId(); Objectother$id=other.getId(); if(this$id==null){ if(other$id!=null){ returnfalse; } }elseif(!this$id.equals(other$id)){ returnfalse; } returntrue; } } } protectedbooleancanEqual(finalObjectother){ returnotherinstanceofDataExample; } publicinthashCode(){ intPRIME=true; intresult=1; Object$id=this.getId(); intresult=result*59+($id==null?43:$id.hashCode()); returnresult; } publicStringtoString(){ return"DataExample(id="+this.getId()+",name="+this.getName()+",age="+this.getAge()+")"; } }@Value使用@Value注解可以把类声明为不可变的,声明后此类相当于final类,无法被继承,其属性也会变成final属性。

/** *Createdbymacroon2020/12/17. */ @Value publicclassValueExample{ privateLongid; privateStringname; privateIntegerage; publicstaticvoidmain(String[]args){ //只能使用全参构造器 ValueExampleexample=newValueExample(1L,"test",20); //example.setName("andy")//没有生成setter方法,会报错 //example.name="andy"//字段被设置为final类型,会报错 } }编译后Lombok会生成如下代码。

publicfinalclassValueExample{ privatefinalLongid; privatefinalStringname; privatefinalIntegerage; publicstaticvoidmain(String[]args){ newValueExample(1L,"test",20); } publicValueExample(finalLongid,finalStringname,finalIntegerage){ this.id=id; this.name=name; this.age=age; } publicLonggetId(){ returnthis.id; } publicStringgetName(){ returnthis.name; } publicIntegergetAge(){ returnthis.age; } }@Builder使用@Builder注解可以通过建造者模式来创建对象,建造者模式加链式调用,创建对象太方便了!/** *Createdbymacroon2020/12/17. */ @Builder @ToString publicclassBuilderExample{ privateLongid; privateStringname; privateIntegerage; publicstaticvoidmain(String[]args){ BuilderExampleexample=BuilderExample.builder() .id(1L) .name("test") .age(20) .build(); System.out.println(example); } }编译后Lombok会生成如下代码。

publicclassBuilderExample{ privateLongid; privateStringname; privateIntegerage; BuilderExample(finalLongid,finalStringname,finalIntegerage){ this.id=id; this.name=name; this.age=age; } publicstaticBuilderExample.BuilderExampleBuilderbuilder(){ returnnewBuilderExample.BuilderExampleBuilder(); } publicStringtoString(){ return"BuilderExample(id="+this.id+",name="+this.name+",age="+this.age+")"; } publicstaticclassBuilderExampleBuilder{ privateLongid; privateStringname; privateIntegerage; BuilderExampleBuilder(){ } publicBuilderExample.BuilderExampleBuilderid(finalLongid){ this.id=id; returnthis; } publicBuilderExample.BuilderExampleBuildername(finalStringname){ this.name=name; returnthis; } publicBuilderExample.BuilderExampleBuilderage(finalIntegerage){ this.age=age; returnthis; } publicBuilderExamplebuild(){ returnnewBuilderExample(this.id,this.name,this.age); } publicStringtoString(){ return"BuilderExample.BuilderExampleBuilder(id="+this.id+",name="+this.name+",age="+this.age+")"; } } }@SneakyThrows还在手动捕获并抛出异常?使用@SneakyThrows注解自动实现试试!/** *Createdbymacroon2020/12/17. */ publicclassSneakyThrowsExample{ //自动抛出异常,无需处理 @SneakyThrows(UnsupportedEncodingException.class) publicstaticbyte[]str2byte(Stringstr){ returnstr.getBytes("UTF-8"); } publicstaticvoidmain(String[]args){ Stringstr="HelloWorld!"; System.out.println(str2byte(str).length); } }编译后Lombok会生成如下代码。

publicclassSneakyThrowsExample{ publicSneakyThrowsExample(){ } publicstaticbyte[]str2byte(Stringstr){ try{ returnstr.getBytes("UTF-8"); }catch(UnsupportedEncodingExceptionvar2){ throwvar2; } } }@Synchronized当我们在多个线程中访问同一资源时,往往会出现线程安全问题,以前我们往往使用synchronized关键字修饰方法来实现同步访问。

使用@Synchronized注解同样可以实现同步访问。

packagecom.macro.mall.tiny.example; importlombok.*; /** *Createdbymacroon2020/12/17. */ @Data publicclassSynchronizedExample{ @NonNull privateIntegercount; @Synchronized @SneakyThrows publicvoidreduceCount(Integerid){ if(count>0){ Thread.sleep(500); count--; System.out.println(String.format("thread-%dcount:%d",id,count)); } } publicstaticvoidmain(String[]args){ //添加@Synchronized三个线程可以同步调用reduceCount方法 SynchronizedExampleexample=newSynchronizedExample(20); newReduceThread(1,example).start(); newReduceThread(2,example).start(); newReduceThread(3,example).start(); } @RequiredArgsConstructor staticclassReduceThreadextendsThread{ @NonNull privateIntegerid; @NonNull privateSynchronizedExampleexample; @Override publicvoidrun(){ while(example.getCount()>0){ example.reduceCount(id); } } } }编译后Lombok会生成如下代码。

publicclassSynchronizedExample{ privatefinalObject$lock=newObject[0]; @NonNull privateIntegercount; publicvoidreduceCount(Integerid){ try{ synchronized(this.$lock){ if(this.count>0){ Thread.sleep(500L); Integervar3=this.count; Integervar4=this.count=this.count-1; System.out.println(String.format("thread-%dcount:%d",id,this.count)); } } }catch(Throwablevar7){ throwvar7; } } }@With使用@With注解可以实现对原对象进行克隆,并改变其一个属性,使用时需要指定全参构造方法。

@With @AllArgsConstructor publicclassWithExample{ privateLongid; privateStringname; privateIntegerage; publicstaticvoidmain(String[]args){ WithExampleexample1=newWithExample(1L,"test",20); WithExampleexample2=example1.withAge(22); //将原对象进行clone并设置age,返回false System.out.println(example1.equals(example2)); } }编译后Lombok会生成如下代码。

publicclassWithExample{ privateLongid; privateStringname; privateIntegerage; publicWithExamplewithId(finalLongid){ returnthis.id==id?this:newWithExample(id,this.name,this.age); } publicWithExamplewithName(finalStringname){ returnthis.name==name?this:newWithExample(this.id,name,this.age); } publicWithExamplewithAge(finalIntegerage){ returnthis.age==age?this:newWithExample(this.id,this.name,age); } publicWithExample(finalLongid,finalStringname,finalIntegerage){ this.id=id; this.name=name; this.age=age; } }@Getter(lazy=true)当我们获取某一个属性比较消耗资源时,可以给@Getter添加lazy=true属性实现懒加载,会生成DoubleCheckLock样板代码对属性进行懒加载。

/** *Createdbymacroon2020/12/17. */ publicclassGetterLazyExample{ @Getter(lazy=true) privatefinaldouble[]cached=expensive(); privatedouble[]expensive(){ double[]result=newdouble[1000000]; for(inti=0;icached=newAtomicReference(); publicGetterLazyExample(){ } privatedouble[]expensive(){ double[]result=newdouble[1000000]; for(inti=0;i



請為這篇文章評分?