1.简介
Lombok是一种Java的实用工具,可以用来帮助开发人员消除Java的冗长,尤其是对于简单Java对象(POJO)。它通过注解实现这一目的。
2.原理
Lombok的所有注解都是编译注解。Lombok工具是运行在编译时解析的。
首先java文件会调用javac编译,在编译后会生成抽象语法树(AST),之后会调用插入式注解处理器处理,插入式注解处理器会修改语法树,生成一些额外的代码,经过处理器的处理语法树会有变动,有变动之后,会再次生成抽象语法树的处理环节,将变动后的代码再次生成抽象语法树,接着再通过注解处理器,如果这次语法树没有被修改,那么就会生成响应的字节码,变成class文件,以上就是注解处理器在整个javac编译源代码生成class文件中起到的作用。
3.安装
1.IDEA
1.在pom文件中引入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
2.安装lombok插件
点击 File->Settings->Plugins,搜索lombok,然后点击Search in repositories
然后选择名字为Lombok的插件,点击安装
插件安装完成之后一定要记得重启IDEA。
重启之后Lombok插件就生效了。
测试例子:
@Getter
@Setter
@ToString
public class TestReq {
/**
* 测试参数1
*/
private String param1;
/**
* 测试参数2
*/
private String param2;
public static void main(String[] args){
TestReq req = new TestReq();
req.setParam1("123");
req.setParam2("456");
System.out.println(req.toString());
}
}
打印结果:
TestReq(param1=123, param2=456)
2.eclipse
1.在pom文件中引入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
2.找到maven仓库中的lombok-1.18.8.jar
目录:maven-repository->org->projectlombok->lombok->1.18.8
在当前目录进入命令行,运行命令:java -jar .\lombok-1.18.8.jar
此时会打开lombok插件安装的可视化界面。
此时可以等待插件自己扫描目录,也可以点击Specify location自己指定目录
等出现eclipse.exe的目录之后,点击Install安装。
安装完成之后,点击Quit Installer退出,并且重启eclipse。
此时安装完成。进行测试
@Getter
@Setter
@ToString
public class TestReq {
/**
* 测试参数1
*/
private String param1;
/**
* 测试参数2
*/
private String param2;
public static void main(String[] args){
TestReq req = new TestReq();
req.setParam1("123");
req.setParam2("456");
System.out.println(req.toString());
}
}
打印结果:
TestReq(param1=123, param2=456)
4.注解
思维导图
1.日志类
1.源码
Lombok包含的日志注解一共有6类8种:
1.apachecommons:@CommonsLog
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
2.flogger:@Flogger
private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();
3.java:@Log
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
4.jbosslog:@JBossLog
private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
5.log4j:@Log4j,@Log4j2
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
6.slf4j:@Slf4j,@XSlf4j
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
8种注解唯一不同的只是声明的日志对象不同,其他都是一样的。用的时候根据自己使用的日志对象来选择注解。
lombok的注解仅仅针对编译阶段,它的作用只是在class文件中添加了声明对象的代码,其他没有任何作用。
@Log4j注解类源码:
/**
* 生成日志类属性
* 完整的的文档地址:<a href="https://projectlombok.org/features/Log">
* 此注解会生成以下代码:
* private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
* 此注解对类和枚举类有效
* 此注解生成的是org.apache.log4j.Logger对象
* @see <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html">org.apache.log4j.Logger</a>
* @see <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Logger.html#getLogger(java.lang.Class)">
*/
//此注解的类型时SOURCE,源码级别的,只有java源码能看到
//在class中只有声明对象的代码,没有注解
@Retention(RetentionPolicy.SOURCE)
//修饰的类型时TYPE:Class, interface (including annotation type), or enum declaration
@Target(ElementType.TYPE)
public @interface Log4j {
/** 这个参数用来声明日志对象构造器传入的类的全限定名,默认使用被打注解的类对象 */
String topic() default "";
}
2.用法
样例1:无参
源码:
@Log4j
public class TestLogOne {}
class:
public class TestLogOne
{
private static final Logger log = Logger.getLogger(TestLogOne.class);
}
样例2:topic参数
@Log4j(topic = "com.ding.test.lombok.log.TestLogTwo")
public class TestLogOne {}
class:
public class TestLogOne
{
private static final Logger log = Logger.getLogger("com.ding.test.lombok.log.TestLogTwo");
}
Logger类的部分源码:
/**
Retrieve the appropriate {@link Logger} instance.
*/
public
static
Logger getLogger(final String name) {
// Delegate the actual manufacturing of the logger to the logger repository.
return getLoggerRepository().getLogger(name);
}
/**
Retrieve the appropriate {@link Logger} instance.
*/
public
static
Logger getLogger(final Class clazz) {
// Delegate the actual manufacturing of the logger to the logger repository.
return getLoggerRepository().getLogger(clazz.getName());
}
从源码可以看出,传入类的全限定名和传入类对象是一致的。
3.缺点
权限修饰词默认为private,无法进行修改。涉及到继承实现等逻辑时无法使用同一个日志对象。
2.@Getter和@Setter
1.源码
@Getter
/**
* 生成getter方法
* 完整参考网址: <a href="https://projectlombok.org/features/GetterSetter">
* 也可以使用@onMethod注解来给方法上加注解
* Example:
* private @Getter int foo;
* will generate:
* public int getFoo() {
* return this.foo;
* }
* 此注解可以用于属性,也可以用于类
* 当用于类时,类中所有非静态的属性都有此注解
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
/**
* 如果你想让你的get方法不是公开的,可以使用此参数配置
*/
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
/**
* 这里列出的所有的注解都会放在生成的方法上
* 此功能的语法取决于JDK的版本
* up to JDK7:<br>
* {@code @Getter(onMethod=@__({@AnnotationsGoHere}))}<br>
* from JDK8:<br>
* {@code @Getter(onMethod_={@AnnotationsGohere})} // note the underscore after {@code onMethod}.
*/
AnyAnnotation[] onMethod() default {};
//是否开启懒加载
//此参数修饰的属性必须时private final类型
boolean lazy() default false;
/**
* 占位符注解,可以将注解放到生成的方法上
* 请阅读文档,不建议使用
*/
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
@interface AnyAnnotation {}
}
@Setter
/**
* 生成setter方法
* Example:
* private @Setter int foo;
* will generate:
* public void setFoo(int foo) {
* this.foo = foo;
* }
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
lombok.AccessLevel value() default lombok.AccessLevel.PUBLIC;
AnyAnnotation[] onMethod() default {};
//这个参数的用法和onMethod基本一致
//onMethod用于方法,onParam用于参数
AnyAnnotation[] onParam() default {};
@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({})
@interface AnyAnnotation {}
}
2.样例
样例1:无参
源码:
@Getter
@Setter
public class TestModel {
private String param1;
private String param2;
}
class:
public class TestModel
{
private String param1;
private String param2;
public void setParam1(String param1)
{
this.param1 = param1;
}
public void setParam2(String param2)
{
this.param2 = param2;
}
public String getParam1()
{
return this.param1;
}
public String getParam2()
{
return this.param2;
}
}
样例2:AccessLevel
源码:
@Getter(AccessLevel.PROTECTED)
@Setter(AccessLevel.PACKAGE)
public class TestModel2 {
private String param1;
private String param2;
}
class:
public class TestModel2
{
private String param1;
private String param2;
void setParam1(String param1)
{
this.param1 = param1;
}
void setParam2(String param2)
{
this.param2 = param2;
}
protected String getParam1()
{
return this.param1;
}
protected String getParam2()
{
return this.param2;
}
}
样例3:onMethod、onParam、lazy
源码:
public class TestModel3<param2> {
@Getter(onMethod_={@Inject},lazy = true)
private final String param1 = "123";
@Setter(onMethod_={@Inject},onParam_={@Param("newName")})
private String param2;
}
class:
public class TestModel3<param2>
{
@Inject
public String getParam1()
{
Object value = this.param1.get();
if (value == null) {
synchronized (this.param1)
{
value = this.param1.get();
if (value == null)
{
String actualValue = "123";value = "123" == null ? this.param1 : "123";this.param1.set(value);
}
}
}
return (String)(value == this.param1 ? null : value);
}
private final AtomicReference<Object> param1 = new AtomicReference();
private String param2;
@Inject
public void setParam2(@Param("newName") String param2)
{
this.param2 = param2;
}
}
3.@ToString
1.源码
/**
* 生成toString方法
* 完整文档地址:<a href="https://projectlombok.org/features/ToString">
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface ToString {
/**
* 打印时包括每个字段的名称,默认true
*/
boolean includeFieldNames() default true;
/**
* 在此参数中列出的字段将不会出现在toString方法中。
* 与of参数互斥,将被废弃
*/
String[] exclude() default {};
/**
* 此参数中出现的字段将会出现在toString方法中
* 与exclude互斥,将被废弃
*/
String[] of() default {};
/**
* 是否调用父类的toString方法
* 默认为false
*/
boolean callSuper() default false;
/**
* 当值为false时,如果get方法可用,toString方法会调用get方法,没有才会直接调用属性。
* 当值为true时,不管有没有get方法,都直接调用属性。
* 默认为false
*/
boolean doNotUseGetters() default false;
/**
* toString方法中只包含被@ToString.Include标记的字段
* 默认包含全部非静态字段
*/
boolean onlyExplicitlyIncluded() default false;
/**
* 此注解标记的字段将不会出现在toString方法中
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Exclude {}
/**
* 标记在属性上,会调用属性的值
* 标记在方法上,会调用方法的返回值
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Include {
/**
* 打印的优先级,数字越高,越优先打印
*/
int rank() default 0;
/**
* 打印出来的字段名
*/
String name() default "";
}
}
2.样例
样例1:callSuper 、includeFieldNames
源码:
@ToString
public class TestModel {
private String param1;
@ToString(callSuper = true,includeFieldNames = true)
class SubModel extends TestModel{
private String param3;
}
@ToString(includeFieldNames = false)
class SubModel2 extends TestModel{
private String param5;
}
}
class:
public class TestModel
{
private String param1;
public String toString()
{
return "TestModel(param1=" + this.param1 + ")";
}
class SubModel
extends TestModel
{
private String param3;
SubModel() {}
public String toString()
{
return "TestModel.SubModel(super=" + super.toString() + ", param3=" + this.param3 + ")";
}
}
class SubModel2
extends TestModel
{
private String param5;
SubModel2() {}
public String toString()
{
return "TestModel.SubModel2(" + this.param5 + ")";
}
}
}
样例2:of、exclude
源码:
@ToString(of = {"param1"})
public class TestModel2{
private String param1;
private String param2;
@ToString(exclude = "param3")
class SubModel extends TestModel2{
private String param3;
private String param4;
}
}
class:
public class TestModel2
{
private String param1;
private String param2;
public String toString()
{
return "TestModel2(param1=" + this.param1 + ")";
}
class SubModel
extends TestModel2
{
private String param3;
private String param4;
SubModel() {}
public String toString()
{
return "TestModel2.SubModel(param4=" + this.param4 + ")";
}
}
}
样例3:onlyExplicitlyIncluded 、Exclude、Include
源码:
@ToString
public class TestModel3{
@Exclude
private String param1;
@Include
private String param2;
private String param3;
@ToString(onlyExplicitlyIncluded = true)
class SubModel extends TestModel3{
@Exclude
private String param4;
@Include
private String param5;
private String param6;
}
}
class:
public class TestModel3
{
private String param1;
private String param2;
private String param3;
public String toString()
{
return "TestModel3(param2=" + this.param2 + ", param3=" + this.param3 + ")";
}
class SubModel
extends TestModel3
{
private String par