lombox详解
lombox简介
Project Lombok 是一个 Java 库,可自动插入您的编辑器并构建工具,不用再编写 getter 或 equals 方法,通过一个注释,您的类就有一个功能齐全的构建器,自动化您的日志变量等等。
为什么要使用lombox
lombox可以通过注解帮我们简化javabean代码。使用Lombok需要删除在项目中JavaBean已经生成的getter方法setter方法以及equals和hash方法,同时Lombok也提供了对打印日志的处理,这样在使用Lombok以后就会大大减少项目中的代码量,同时由于Lombok有自动修改的功能,这也提供了项目中代码的执行效率。
使用lombox
-
Eclipse
eclipse使用lombox需要先下载lombox的jar包,然后进行安装就可以使用了
-
IDEA
IDEA中使用lombox的话,无需安装插件,idea内置集成了lombox插件,只需在pom.xml文件中添加依赖即可。 <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> <scope>provided</scope> </dependency> </dependencies>
lombox相关注解
在使用lombox的注解前,先准备好一个实体类,笔者这里采用学生类(Student)来作为演示。
public class Student implements Serializable {
private String username;// 名称
private String password;// 密码
private String phone;// 手机
private String address;// 地址
}
@AllArgsConstructor
该注解使用在类上,或者在类javabean中使用,作用是生成对应的有参构造方法,默认是全部属性的。
-
演示代码
@AllArgsConstructor public class Student implements Serializable { private String username; private String password; private String phone; private String address; }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; private String address; public Student(String username, String password, String phone, String address) { this.username = username; this.password = password; this.phone = phone; this.address = address; } }
-
在学生类中添加一个Car类
@AllArgsConstructor public class Student implements Serializable { private String username; private String password; private String phone; private String address; @AllArgsConstructor class Car{ private String brandName; } }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; private String address; public Student(String username, String password, String phone, String address) { this.username = username; this.password = password; this.phone = phone; this.address = address; } class Car { private String brandName; public Car(String brandName) { this.brandName = brandName; } } }
@NoArgsConstructor
该注解使用在类上,或类JavaBean中使用,作用是生成对应的无参构造方法。
-
演示代码
@NoArgsConstructor public class Student implements Serializable { private String username; private String password; private String phone; private String address; @NoArgsConstructor class Car{ private String brandName; } }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; private String address; public Student() { } class Car { private String brandName; public Car() { } } }
@RequiredArgsConstructor
该注解使用在类上,或类JavaBean中使用,作用是生成构造方法(带参数或者不带参数)。如果要带参数,这参数只能是以 final 修饰的未经初始化的字段或者是以 @NonNull 注解的未经初始化的字段。单独在类上标注没有什么效果。次注解还可以使用staticName属性,来生成一个指定名称的静态方法,返回一个调用相应的构造方法产生的对象。
@Nonll注解用于在属性或构造器上,Lombok会生成一个非空的声明,可用于校验参数,能帮助避免空指针,相当于数据校验。
-
单独使用标注在类上,代码演示
@RequiredArgsConstructor public class Student implements Serializable { private String username; private String password; private String phone; private String address; @RequiredArgsConstructor class Car{ private String brandName; } }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; private String address; public Student() { } class Car { private String brandName; public Car() { } } }
-
配合@NonNull注解和常量字段,来生成带参数的构造方法,代码演示
@RequiredArgsConstructor public class Student implements Serializable { private String username; private String password; @NonNull // 标注手机不能为空 private String phone; private final String address;//地址被final修饰 }
-
编译后
public class Student implements Serializable { private String username; private String password; @NonNull private String phone; private final String address; public Student(@NonNull String phone, String address) { if (phone == null) { throw new NullPointerException("phone is marked non-null but is null"); } else { this.phone = phone; this.address = address; } } }
-
配合staticName属性来指定生成一个静态方法,代码演示
@RequiredArgsConstructor(staticName = "showStudentInfo") public class Student implements Serializable { private String username; private String password; @NonNull private String phone; private final String address; }
-
编译后
public class Student implements Serializable { private String username; private String password; @NonNull private String phone; private final String address; private Student(@NonNull String phone, String address) { if (phone == null) { throw new NullPointerException("phone is marked non-null but is null"); } else { this.phone = phone; this.address = address; } } public static Student showStudentInfo(@NonNull String phone, String address) { return new Student(phone, address); } }
以上这三个构造函数相关的注解,都有一个访问级别这样的一个属性(access:默认级别为public)
-
构造方法默认的级别
-
指定默认级别为私有,代码演示
@AllArgsConstructor(access = AccessLevel.PRIVATE) public class Student implements Serializable { private String username; private String password; private String phone; private String address; }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; private String address; private Student(String username, String password, String phone, String address) { this.username = username; this.password = password; this.phone = phone; this.address = address; } }
-
AccessLevel枚举设置的访问级别如下:
NONE: 表示不生成任何内容或完全没有方法。 PUBLIC :表明该数据成员,成员函数是对所有用户开放的,所有用户都可以直接进行调用 MODULE:module和package的效果是一样的。 PROTECTED:对于子女(子孙类)、朋友(同package)来说,就是public的,可以自由使用,没有任何限制,而对于其他的外部class,protected就变成private。 PACKAGE:同包下可以访问。 PRIVATE:表示私有,私有的意思就是除了class自己之外,任何人都不可以直接使用,私有财产神圣不可侵犯嘛,即便是子女、朋友、都不可以使用。
-
为什么说Module和package的效果相同?
public static int toJavacModifier(AccessLevel accessLevel) { switch (accessLevel) { case MODULE: case PACKAGE: return 0; default: case PUBLIC: return Flags.PUBLIC; case NONE: case PRIVATE: return Flags.PRIVATE; case PROTECTED: return Flags.PROTECTED; } }
@Getter/@Setter
使用在类上或者类javabean中,作用是生成对应的getter/setter方法。乜可以指定访问级别,如果使用了@Setter/@Getter注解,又自己写了,那么以自己写的优先级高
-
代码演示
@Setter @Getter public class Student implements Serializable { private String username; private String password; @Setter(value = AccessLevel.NONE)//什么都不生成 private String phone; public String getUsername() { return "这是我自己写的get方法"; } }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; public Student() { } public String getUsername() { return "这是我自己写的get方法"; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public String getPassword() { return this.password; } public String getPhone() { return this.phone; } }
@ToString
该注解使用在类上或者类javabean中,作用是自动生成对应的toString方法,默认是全部字段的,可以通过of属性来指定哪些属性加入到toString方法中,同时乜可以使用exclude属性来排除。
-
代码演示
@ToString public class Student implements Serializable { private String username; private String password; private String phone; private String address; }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; private String address; public Student() { } public String toString() { return "Student(username=" + this.username + ", password=" + this.password + ", phone=" + this.phone + ", address=" + this.address + ")"; } }
-
通过of属性来指定生成toString方法,代码演示
@ToString(of = {"username","phone","address"}) public class Student implements Serializable { private String username; private String password; private String phone; private String address; }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; private String address; public Student() { } public String toString() { return "Student(username=" + this.username + ", phone=" + this.phone + ", address=" + this.address + ")"; } }
-
通过exclude属性来排除指定的属性不加入toString方法中,代码演示
@ToString(exclude = {"password"}) public class Student implements Serializable { private String username; private String password; private String phone; }
-
编译后
public class Student implements Serializable { private String username; private String password; private String phone; public Student() { } public String toString() { return "Student(username=" + this.username + ", phone=" + this.phone + ")"; } }
@EqualsAndHashCode
该注解使用在类上,或者类javabean上。作用是会自动重写equals/hashCode方法。默认是使用所有的属性来重写equals/hashCode方法。可以通过of属性来指定属性生成。
-
代码演示
@EqualsAndHashCode public class Student implements Serializable { private String username; private String password; }
-
编译后
public class Student implements Serializable { private String username; private String password; public Student() { } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof Student)) { return false; } else { Student other = (Student)o; if (!other.canEqual(this)) { return false; } else { Object this$username = this.username; Object other$username = other.username; if (this$username == null) { if (other$username != null) { return false; } } else if (!this$username.equals(other$username)) { return false; } Object this$password = this.password; Object other$password = other.password; if (this$password == null) { if (other$password != null) { return false; } } else if (!this$password.equals(other$password)) { return false; } return true; } } } protected boolean canEqual(Object other) { return other instanceof Student; } public int hashCode() { int PRIME = true; int result = 1; Object $username = this.username; int result = result * 59 + ($username == null ? 43 : $username.hashCode()); Object $password = this.password; result = result * 59 + ($password == null ? 43 : $password.hashCode()); return result; } }
-
通过of属性来指定,代码演示
@EqualsAndHashCode(of = {"username"}) public class Student implements Serializable { private String username; private String password; }
-
编译后
public class Student implements Serializable { private String username; private String password; public Student() { } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof Student)) { return false; } else { Student other = (Student)o; if (!other.canEqual(this)) { return false; } else { Object this$username = this.username; Object other$username = other.username; if (this$username == null) { if (other$username != null) { return false; } } else if (!this$username.equals(other$username)) { return false; } return true; } } } protected boolean canEqual(Object other) { return other instanceof Student; } public int hashCode() { int PRIME = true; int result = 1; Object $username = this.username; int result = result * 59 + ($username == null ? 43 : $username.hashCode()); return result; } }
@Data注解
该注解使用在类上或者类javabean上,作用是包含了@Getter/@Setter/@ToString/@EqualAndHashCode/@RequiredArgsConstructor,需要注意的是@Data注解并没有包含@NoArgsConstructor
-
代码演示
@Data public class Student implements Serializable { private String username; private String password; }
-
编译后
public class Student implements Serializable { private String username; private String password; public Student() { } public String getUsername() { return this.username; } public String getPassword() { return this.password; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof Student)) { return false; } else { Student other = (Student)o; if (!other.canEqual(this)) { return false; } else { Object this$username = this.getUsername(); Object other$username = other.getUsername(); if (this$username == null) { if (other$username != null) { return false; } } else if (!this$username.equals(other$username)) { return false; } Object this$password = this.getPassword(); Object other$password = other.getPassword(); if (this$password == null) { if (other$password != null) { return false; } } else if (!this$password.equals(other$password)) { return false; } return true; } } } protected boolean canEqual(Object other) { return other instanceof Student; } public int hashCode() { int PRIME = true; int result = 1; Object $username = this.getUsername(); int result = result * 59 + ($username == null ? 43 : $username.hashCode()); Object $password = this.getPassword(); result = result * 59 + ($password == null ? 43 : $password.hashCode()); return result; } public String toString() { return "Student(username=" + this.getUsername() + ", password=" + this.getPassword() + ")"; } }
@Accessors
该注解使用在类上,或类javabean上,作用是用于生成 getter 和 setter 的设置容器,目的是为了打破bean规范,以便最终获得更好看的 API。
该注解里面有3个属性分别是:
- fluent:布尔值,默认为false,如果为
true
,访问器将以字段命名(命名为fieldName()),并且不包含get或set前缀。 如果 true 和chain被省略,则chain默认为true 。- chain:布尔值,如果为
true
,setter 将返回this而不是void 。 默认值:false ,除非fluent=true- prefix:字符串列表,默认空,如果存在,则具有指定前缀的字段才会进行 getter/setter 处理,需要注意的啊,前缀仅在下一个字符不是小写字符或前缀的最后一个字母不是字母(例如下划线)时才计算在内。 如果去掉前缀后多个字段都变成同名,就会报错。
-
演示fluent属性为true,chain被省略,则chain默认为true,代码演示
@Accessors(fluent = true) @Setter public class Student implements Serializable { private String username; private String password; }
-
编译后
public class Student implements Serializable { private String username; private String password; public Student() { } public Student username(String username) { this.username = username; return this; } public Student password(String password) { this.password = password; return this; } }
-
使用
public static void main(String[] args) { Student student = new Student(); student.username("Hello").password("world"); }
-
演示prefix前缀,字符串列表,指定前缀为zz的字段才会进行处理,带猫眼石
@Accessors(prefix = "zz") @Setter public class Student implements Serializable { private String zzUsername; private String zzPassword; }
-
编译后
public class Student implements Serializable { private String zzUsername; private String zzPassword; public Student() { } public void setUsername(String zzUsername) { this.zzUsername = zzUsername; } public void setPassword(String zzPassword) { this.zzPassword = zzPassword; } }
-
演示前缀仅在下一个字符不是小写字符或前缀的最后一个字母不是字母(例如下划线)时才计算在内
@Accessors(prefix = "zz") @Setter public class Student implements Serializable { private String zzusername; private String zzpassword; }
-
编译后
public class Student implements Serializable { private String zzusername; private String zzpassword; public Student() { } }
@Builder
该注解使用在类上或者类javabean中,使用后效果和@Accessors效果类似,只是生成的代码采用的是建造者模式生成的。这种好处在于一步步创建一个对象,对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。减少对象创建过程中引入的多个构造函数、可选参数以及多个setter过度使用导致的不必要的复杂性。看起来很整齐,先赋值,后创建对象,最终调用build()方法才创建了构建类的对象。
-
代码演示
@Builder public class Student implements Serializable { private String username; private String password; private final String phone; }
-
编译后
public class Student implements Serializable { private String username; private String password; private final String phone; Student(String username, String password, String phone) { this.username = username; this.password = password; this.phone = phone; } public static Student.StudentBuilder builder() { return new Student.StudentBuilder(); } public static class StudentBuilder { private String username; private String password; private String phone; StudentBuilder() { } public Student.StudentBuilder username(String username) { this.username = username; return this; } public Student.StudentBuilder password(String password) { this.password = password; return this; } public Student.StudentBuilder phone(String phone) { this.phone = phone; return this; } public Student build() { return new Student(this.username, this.password, this.phone); } public String toString() { return "Student.StudentBuilder(username=" + this.username + ", password=" + this.password + ", phone=" + this.phone + ")"; } } }
-
通过编译后代码,来看看@Builder注解帮我们做了什么
1.创建了一个名为StudentBuilder的内部静态类,同时具有和实体类相同的属性(构建器) 2.对于目标类中的所有的属性和未初始化的final字段,都会内部静态类在中创建对应属性 3.创建一个无参的default构造函数 4.对于实体类中的每个参数,都会对应创建类似于setter的方法,只不过方法名与该参数名相同。 并且返回值是本身(便于链式调用) 5.一个build()方法,调用此方法,就会根据设置的值进行创建实体对象 6.在内部静态类中生成toString方法,实体类中不会生成 7.在实体类中创建一个builder方法,目的是创建构建器(通过调用builder方法去创建一个静态内部类对象)
-
使用
public static void main(String[] args) { Student student = Student.builder().username("Hello"). password("world").phone("17673499345").build(); }
@Log/@Slf4j
注解用于需要打印日志的类中使用,当项目中使使用log4j使用@Log注解,项目中使用的是slf4j使用@Slf4j注解,可以帮我们简化日志的打印流程,帮我内置日志对象,只需要调用方法即可。
-
加入依赖
<!--logback依赖--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
-
创建service类,使用
slf4j
作为日志门面public class StudentService { private static final Logger LOGGER = LoggerFactory.getLogger(StudentService.class); // 查询所有的学生 public List<Student> findAllStudent() { LOGGER.info("{},执行了findAllStudent方法。", LocalDateTime.now()); return new ArrayList<Student>(); } }
-
以上是我们经常这样使用的,这样我们类里面就会出现
如下
代码,可以使用@Slf4j注解来代码。private static final Logger LOGGER = LoggerFactory.getLogger(StudentService.class);
@Slf4j public class StudentService { // 查询所有的学生 public List<Student> findAllStudent() { log.info("{},执行了findAllStudent方法。", LocalDateTime.now()); return new ArrayList<Student>(); } } /* 相当于帮我们写了这样一行代码 private static final org.slf4j.Logger log =org.slf4j.LoggerFactory.getLogger(StudentService.class); */
-
执行结果
-
使用@Log注解
@Log public class StudentService { // 查询所有的学生 public List<Student> findAllStudent() { log.info("执行了findAllStudent方法。"); return new ArrayList<Student>(); } }
-
执行结果
@Cleanup
该注解用于自动资源管,能够帮我们自动调用
close()
方法,可以使用在任何局部变量声明来使用
-
代码演示
public static void main(String[] args) throws IOException { @Cleanup InputStream in = new FileInputStream(args[0]); in.read(); }
-
编译后
public static void main(String[] args) throws IOException { FileInputStream in = new FileInputStream(args[0]); try { in.read(); } finally { if (Collections.singletonList(in).get(0) != null) { in.close(); } } }
@Value
该注解作用是标注一个类是不可变类,不可变类是指创建该类的实例后,该实例的实例变量是不可改变的。像java提供的8个包装类和String都是不可变类。
不可变类遵守如下规则:
- 使用private和final修饰符来修饰该类的成员变量
- 提供带参数的构造器,根据传入的参数来初始化类里的成员变量
- 仅为该类的成员变量提供getter方法,不要提供setter方法,因为普通方法无法修改final修饰的成员变量
- 如果有必要,重写Object类的hascode()和equals()方法。equals方法根据关键成员变量来作为两个对象是否相等的标准,除此之外,还应该保证两个用equals方法判断为相等的对象的hashCode()也相等。
@Value注解包含 @ToString/@EqualsAndHashCode/@AllArgsConstructor/@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)/@Getter
-
代码演示
@Value public class Student implements Serializable { private String username; private String password; private String phone; }
-
编译后
public final class Student implements Serializable { private final String username; private final String password; private final String phone; public Student(String username, String password, String phone) { this.username = username; this.password = password; this.phone = phone; } public String getUsername() { return this.username; } public String getPassword() { return this.password; } public String getPhone() { return this.phone; } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof Student)) { return false; } else { Student other; label44: { other = (Student)o; Object this$username = this.getUsername(); Object other$username = other.getUsername(); if (this$username == null) { if (other$username == null) { break label44; } } else if (this$username.equals(other$username)) { break label44; } return false; } Object this$password = this.getPassword(); Object other$password = other.getPassword(); if (this$password == null) { if (other$password != null) { return false; } } else if (!this$password.equals(other$password)) { return false; } Object this$phone = this.getPhone(); Object other$phone = other.getPhone(); if (this$phone == null) { if (other$phone != null) { return false; } } else if (!this$phone.equals(other$phone)) { return false; } return true; } } public int hashCode() { int PRIME = true; int result = 1; Object $username = this.getUsername(); int result = result * 59 + ($username == null ? 43 : $username.hashCode()); Object $password = this.getPassword(); result = result * 59 + ($password == null ? 43 : $password.hashCode()); Object $phone = this.getPhone(); result = result * 59 + ($phone == null ? 43 : $phone.hashCode()); return result; } public String toString() { return "Student(username=" + this.getUsername() + ", password=" + this.getPassword() + ", phone=" + this.getPhone() + ")"; } }
@Synchronized
synchronized是线程安全中的一个关键字,是一种同步锁,主要用来保证在同一个时刻,只有一个线程可以执行某个方法或者某段代码块。一般使用synchronized去锁住代码块,而不是方法,因为锁住代码块效率更高。
@Synchronized注解是synchronized方法修饰符的更安全的变体,该注解只能在静态方法和实例方法上使用。它的操作类似于synchronized关键字,但是它锁定在不同的对象上。关键字(synchronized)锁定在上this,但注解锁定在名为$ lock的字段上,该字段是私有的。
如果该字段不存在,则会为您创建。如果对static方法进行注解,则注解将锁定在名为$ LOCK的静态字段上。
如果需要,可以自己创建这些锁。在
$lock
和$LOCK
领域会当然不会,如果你已经自己原创生成的。您还可以选择锁定另一个字段,方法是将其指定为@Synchronized
注释的参数。在此用法变体中,字段不会自动创建,您必须自己显式创建它们,否则将发出错误。锁定
this
或您自己的类对象可能会产生不幸的副作用,因为不受您控制的其他代码也可能锁定这些对象,这可能导致竞争条件和其他与线程相关的严重错误。
-
代码演示
public class StudentService { private final Object readLock = new Object(); @Synchronized public static void a() { System.out.println("world"); } @Synchronized public int b() { return 42; } @Synchronized("readLock") public void c() { System.out.println("bar"); } }
-
编译后
public class StudentService { private static final Object $LOCK = new Object[0]; private final Object $lock = new Object[0]; private final Object readLock = new Object(); public StudentService() { } public static void a() { synchronized($LOCK) { System.out.println("world"); } } public int b() { synchronized(this.$lock) { return 42; } } public void c() { synchronized(this.readLock) { System.out.println("bar"); } } }
@SneakyThrows
在Spring中我们可以随处可见异常处理手段就是在外包一层RuntimeException,类似如下
try{ }catch(Exception e){ throw new RuntimeException(e); }
@SneakyThrows
就是为了消除这样的模板,可用于抛出已检查的异常,而无需在方法的throws
子句中实际声明。当然,这种有点争议的能力应该谨慎使用。lombok 生成的代码不会忽略、包装、替换或以其他方式修改抛出的检查异常;它只是伪造了编译器。在 JVM(类文件)级别,无论throws
您的方法的子句如何,所有异常,无论是否检查,都可以抛出,这就是为什么它有效。
-
代码演示
public class StudentService { // 指定捕获的异常 @SneakyThrows(UnsupportedEncodingException.class) public String utf8ToString(byte[] bytes) { return new String(bytes, "UTF-8"); } @SneakyThrows public void run() { throw new Throwable(); } }
-
编译后
public class StudentService { public StudentService() { } public String utf8ToString(byte[] bytes) { try { return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException var3) { throw var3; } } public void run() { try { throw new Throwable(); } catch (Throwable var2) { throw var2; } } }
lombox原理应用
lombox实现了java的jsr269规范(插件化的注解处理API)点击查看jsr269规范,除了lombox,常用的MapStruct乜实现了此规范,都是基于SPI服务发现机制实现。
lombox原理
-
源代码文件(source)
-
编译(parse)
-
抽象语法树(AST)
-
注解编译器
-
自动编译匹配(Modified AST)
先编写源代码文件,在经过编译处理后,lombok会使用自己的抽象语法树去进行注解的匹配,
如果在项目中的某一个类中使用了lombok中的注解,那么注解编译器就会自动去匹配项目中的注解对应到在lombok语法树中的注解文件,
并经过自动编译匹配来生成对应类中的getter或者setter等方法,达到简化代码的目的。
原理应用简单模拟
-
定义编译期注解
-
创建编译器的注解处理器
-
打包
在resources文件夹下创建META-INF文件夹,META-INF下创建services文件夹, services下创建文件名为javax.annotation.processing.Processor的文本文件,内容为自定义注解处理器`全限定类名` 添加这个文件是为了将自己添加为Processor,方便其他项目调用,但自己编译时又不需要processor, 但又需要打的包里有services文件夹,这时候需要配置maven插件。
-
创建一个maven项目(我的叫
MyProcessorTest
),添加依赖<build> <resources> <resource> <directory>src/main/resources</directory> <excludes> <exclude>META-INF/**/*</exclude> </excludes> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.6</version> <executions> <execution> <id>process-META</id> <phase>prepare-package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>target/classes</outputDirectory> <resources> <resource> <directory>${basedir}/src/main/resources/</directory> <includes> <include>**/*</include> </includes> </resource> </resources> </configuration> </execution> </executions> </plugin> </plugins> </build>
-
编写编译期注解
@Target(ElementType.TYPE)//只能使用在接口,类,枚举 @Retention(RetentionPolicy.SOURCE)//编译期间,源文件期间保留 public @interface MyCode { }
-
引入commons-io依赖,方便后续的写入信息到文件
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
-
创建编译器的注解处理器
@SupportedAnnotationTypes(value = {"cn.lijie.MyCode"})//扫描的注解 @SupportedSourceVersion(value = SourceVersion.RELEASE_8)//用于指示注释处理器支持的最新源版本的注释 public class MyCodeProcessor extends AbstractProcessor { /** * 处理扫描到的注解,进行处理的方法 * * @param annotations * @param roundEnv * @return */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { String filePath = "D:\MyProcessor.txt"; try { FileUtils.writeStringToFile(new File(filePath), "进入MyCodeProcessor --> process方法\r\n", StandardCharsets.UTF_8, true); annotations.forEach(typeElement -> { try { FileUtils.writeStringToFile(new File(filePath), typeElement.toString() + "\r\n", StandardCharsets.UTF_8, true); } catch (IOException e) { e.printStackTrace(); } }); FileUtils.writeStringToFile(new File(filePath), roundEnv.toString() + "\r\n", StandardCharsets.UTF_8, true); } catch (Exception e) { e.printStackTrace(); } return true; } }
-
在resources文件夹下创建META-INF文件夹,META-INF下创建services文件夹,services下创建文件名为javax.annotation.processing.Processor的文本文件
cn.lijie.MyCodeProcessor
-
打包,得到一个名为MyProcessorTest-1.0-SNAPSHOT.jar
mvn clean package install
-
在其他的项目引入该jar的依赖
<dependency> <groupId>cn.lijie</groupId> <artifactId>MyProcessorTest</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
-
测试使用,添加注解,进行编译。
@MyCode public class MyTest { }
-
编译完后就会在D盘下生成一个MyProcessor.txt文件,打开就能看到我们写入的内容