Un outil qui simplifie le code source et améliore l'efficacité de la programmation, et est utilisé pour générer du code couramment utilisé.
Deux forfaits :
-
Lombok
-
lombok.experimental (propriétés expérimentales)
comment utiliser lombok
Introduire des dépendances
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
installer le plugin
code utilisé dans
@Data
public class People {
private String name;
}
Résumé des annotations courantes
@val |
Lors de la déclaration d'une variable, le type de variable peut être déduit par lui-même, et il est livré avec un attribut final, |
@Données |
@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor |
@Slf4j |
générer un objet de journal |
@Valeur |
Similaire à @Data, avec les deux différences suivantes :
|
@Getter |
Méthodes getter pour toutes les propriétés |
@Setter |
Méthodes setter pour toutes les propriétés |
@Constructeur |
Générer du code pour les constructeurs chaînés |
@Nettoyer |
Libérez ou fermez des ressources en toute sécurité, le scénario le plus courant est l'opération de fermeture de flux dans IO |
@NonNull |
getter : si la propriété obtenue est nulle, lance NPE setter : si la valeur transmise est nulle lors de la définition de la propriété, lancez NPE |
@ToString |
Générer la méthode toString() |
@SneakyThrows |
Mangez l'exception levée et réduisez certains codes de capture d'essai inutiles |
@NoArgsConstructor |
Générer un constructeur sans argument |
@AllArgsConstructor |
Ajouter un constructeur avec toutes les propriétés |
@EqualsAndHashCode |
Générer les méthodes equals() et hashcode() |
@RequiredArgsConstrutor |
Un constructeur contenant des constantes et des variables marquées NotNull sera généré |
l'affaire
La partie commentée est le code que lombok nous aidera à générer automatiquement.
@Slf4j
@Slf4j
public class Test {
// private static final Logger log=LoggerFactory.getLogger(Test.class);
public static void main(String[] args) {
log.info("Hello world");
}
}
@Constructeur
Test test = Test.builder()
.id(id)
.page(page)
.build();
@Data
@Builder
public class Test {
private String id;
private String page;
/**
@java.beans.ConstructorProperties({"id", "page"})
Test(Long id, int page) {
this.id = id;
this.page = page;
}
public static TestBuilder builder() {
return new TestBuilder();
}
public TestBuilder toBuilder() {
return new TestBuilder().id(this.id).page(this.page);
}
public static class TestBuilder {
private Long id;
private int page;
TestBuilder() {}
public TestBuilder id(Long id) {
this.id = id;
return this;
}
public TestBuilder page(int page) {
this.page = page;
return this;
}
public Test build() {
return new Test(id, page);
}
public String toString() {
return "Test.TestBuilder(id=" + this.id + ", page="
+ this.page")";
}
*/
}
@SneakyThrows
@Test(expected = RuntimeException.class)
@SneakyThrows
public void test_throw_exception() {
when(HttpClientUtil.get(anyString()).thenThrow(new RuntimeException());
api.test("nice");
}
@Données
@Data
public class User {
private Long id;
private String username;
private String password;
/**
public User() {}
public Long getId() {return this.id;}
public String getUsername() {return this.username;}
public String getPassword() {return this.password;}
public void setId(Long id) {this.id = id; }
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; }
...
return true;
}
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $id = this.getId();
result = result * PRIME + ($id == null ? 43 : $id.hashCode());
final Object $username = this.getUsername();
...
return result;
}
protected boolean canEqual(Object other) {return other instanceof User;}
public String toString() {
return "User(id=" + this.getId() + ...+ ")";
}
*/
}
}
@Valeur
@Value
public class Test {
(private final) Long id;
(private final) String page;
/**
@java.beans.ConstructorProperties({"id", "page"})
public Test(Long id, String page) {
this.id = id;
this.page = page;
}
public Long getId() {return this.id;}
public String getPage() {return this.page;}
public boolean equals(Object o) {
if (o == this) { return true; }
...
return true;
}
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $id = this.getId();
result = result * PRIME + ($id == null ? 43 : $id.hashCode());
...
return result;
}
public String toString() {
return "Test.TestBuilder(id=" + this.id + ", page="
+ this.page")";
}
*/
}
Convention de code Java
-
[Recommandation] Pour les classes d'entités immuables avec trop de champs ou trop de paramètres de constructeur, il est recommandé d'utiliser lombok pour générer le constructeur
-
[Recommandation] Avant que les données de type Record ne soient prises en charge, il est recommandé que les champs de classe d'entité utilisent des annotations lombok pour générer des setters et des getters, et décident d'utiliser uniquement @Getter, @Setter ou @Data selon le principe de minimisation
-
[Recommandation] En programmation contractuelle, il est recommandé d'utiliser l'annotation non vide de l'annotation IDEA IntelliJ > Spring > Lombok pour indiquer que le paramètre n'est pas vide
avantages et inconvénients
avantage
-
Réduire efficacement la quantité de code
-
dynamique. Par exemple, lors de l'ajout ou de la soustraction de champs, il n'est pas nécessaire de se soucier de la modification de méthodes telles que Getter/Setter
-
Améliore la lisibilité du code dans une certaine mesure
défaut
-
Introduisez des dépendances externes. Une fois que lombok est utilisé dans le package de ressources, d'autres devront installer des plugins s'ils veulent voir votre code source
-
Le code généré n'est pas intuitif et peut ne pas générer le code comme prévu
-
Intégrité réduite du code source
Problème commun
@Constructeur
Problème de valeur par défaut de la propriété. Si vous utilisez @Builder pour générer du code, la valeur par défaut de l'attribut ne sera pas valide. Vous devez utiliser @Builder.Default pour marquer l'attribut avec la valeur par défaut, par exemple :
@Builder
public class UserQueryParam {
private Long id;
private String username;
@Builder.Default
private int page = 1;
@Builder.Default
private int size = 15;
}
@Getter
Traitement spécial des valeurs booléennes
private boolean test;
1. La règle de génération par défaut pour la méthode de lecture des attributs est is+nom d'attribut au lieu de get+nom d'attribut, c'est-à-dire isTest au lieu de getTest
2. Si le nom de l'attribut lui-même commence par is, tel que isTest, la méthode pour obtenir l'attribut est toujours isTest, pas l'embarrassant isIsTest
3. Dans les deux règles ci-dessus, il y aura une situation extrême causée par un caractère déraisonnable, c'est-à-dire qu'il y a deux attributs, l'un est nommé isTest et l'autre est test. Il s'agit essentiellement d'un problème de conception. Pour cette situation, la prise -in La méthode de traitement consiste à générer un seul isTest.Quant à l'attribut lu, cela dépend de l'ordre des attributs, et le premier est prioritaire.
@Valeur et @Données
@Value est souvent utilisé pour les classes immuables. Une classe immuable signifie qu'après la création d'une instance de la classe, les variables d'instance de l'instance ne peuvent pas être modifiées.
Semblable à @Data, il existe deux différences principales :
-
Un constructeur avec des paramètres complets est généré ;
-
Méthodes getter uniquement, pas de méthodes setter ;
@Valeur |
@Données |
|
@Getter |
✅ |
✅ |
@Setter |
❌ |
✅ |
@ToString |
✅ |
✅ |
@EqualsAndHashCode |
✅ |
✅ |
@RequiredArgsConstrutor |
❌ |
✅ |
@AllArgsConstructor |
✅ |
❌ |
@FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE) |
✅ |
❌ |
Les annotations identifiées par @Value ne peuvent pas être utilisées avec @RequestBody et jackson.
La raison pour laquelle la désérialisation de jackson échoue après avoir utilisé @Value et fastjson peut réussir :
1. Jackson lit la méthode set et @Value ne génère pas la méthode set, donc elle échoue ;
2. Fastjson lit le constructeur et @Value génère le constructeur, donc il réussit ;
Extension : lors de l'utilisation de fastjson, utilisez @Builder pour vérifier si @NoArgsConstructor ou @AllArgsConstructor est ajouté
Principe de Lombok
Modifier l'arbre de syntaxe abstraite (AST) au moment de la compilation
@Getter(AccessLevel.PUBLIC)
public class User {
private Long id;
private String username;
@Getter(AccessLevel.PRIVATE)
private String password;
}
public class User {
private Long id;
private String username;
private String password;
public Long getId() {return this.id;}
public String getUsername() {return this.username;
// Field 优先级更高
private String getPassword() {return this.password;}
}
Arbre de syntaxe AST
introduire
Lombok est traité dans le lien de l'arborescence syntaxique AST. AST est une représentation arborescente utilisée pour décrire la structure grammaticale du code du programme. Chaque nœud de l'arbre grammatical représente une structure grammaticale dans le code du programme, telle que package, type, modificateur , opérateurs , les interfaces, les valeurs de retour et même les commentaires de code peuvent tous être une structure grammaticale.
goûter
Sur Idea, vous pouvez également afficher l'arbre de syntaxe AST du code en installant le plug-in, comme indiqué dans la figure ci-dessous
Chaque attribut et chaque méthode à gauche peut trouver le nœud correspondant à droite, ainsi en manipulant les nœuds de l'arbre AST, le code peut être ajouté dynamiquement lors de la compilation.
RSC 269
Depuis Java 6, javac a commencé à prendre en charge la spécification de l'API de traitement des annotations enfichables JSR 269. Tant que le programme implémente l'API, les annotations définies peuvent être appelées lorsque le code source Java est compilé. L'essence de Lombok est de s'appuyer sur JSR 269 pour réaliser l'utilisation du "processeur d'annotations" (outil de traitement des annotations) dans l'étape de compilation Javac pour prétraiter les annotations personnalisées et générer un "fichier de classe" qui est réellement exécuté sur la JVM.
D'après le diagramme schématique ci-dessus, on peut voir que le traitement des annotations est une étape entre l'analyse du code source Java par le compilateur et la génération du fichier de classe.
Lombok
Lombok est essentiellement un programme qui implémente la " JSR 269 API ". Dans le processus d'utilisation de javac, le processus spécifique de sa fonction est le suivant :
-
javac analyse le code source et génère un arbre de syntaxe abstraite (AST)
-
Appelez le programme Lombok qui implémente "JSR 269 API" pendant le fonctionnement
-
À ce moment, Lombok traite l'AST obtenu à la première étape, trouve l'arbre de syntaxe (AST) correspondant à la classe où se trouve l'annotation @Data, puis modifie l'arbre de syntaxe (AST) pour ajouter les nœuds d'arbre correspondants définis par les méthodes getter et setter
-
javac utilise l'arbre de syntaxe abstraite modifié (AST) pour générer des fichiers de bytecode, c'est-à-dire ajouter de nouveaux nœuds (blocs de code) à la classe
Comme le montre l'organigramme de l'exécution de Lombok ci-dessus, une fois que Javac est analysé dans un arbre de syntaxe abstraite AST, Lombok modifie dynamiquement l'AST en fonction du processeur d'annotation écrit par lui-même, en ajoutant de nouveaux nœuds (c'est-à-dire que l'annotation personnalisée Lombok a besoin pour générer du code), et enfin générer un fichier de classe de bytecode exécutable JVM par analyse.
code de base
Plusieurs annotations personnalisées à Lombok ont des classes de traitement de gestionnaire correspondantes. Ce sont ces classes de gestionnaire qui remplacent, modifient et traitent réellement leurs annotations personnalisées à Lombok. Pour plus de détails sur leur mise en œuvre, veuillez vous reporter au code.
plug-in lombok
Lorsque la méthode omise à l'aide de l'annotation Lombok est appelée, une erreur indiquant que la définition est introuvable sera signalée. Dans ce cas, un traitement spécial est nécessaire. Par exemple, dans Intellij Idea, le plug-in Lombok doit être téléchargé et installé.