欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

利用Java手寫一個(gè)簡(jiǎn)易的lombok的示例代碼

 更新時(shí)間:2022年10月14日 15:48:37   作者:野生java研究僧  
Lombok是一款Java開發(fā)插件,使得Java開發(fā)者可以通過其定義的一系列注解來消除業(yè)務(wù)工程中冗長(zhǎng)和繁瑣的代碼,尤其對(duì)于簡(jiǎn)單的Java模型對(duì)象。本文就來手寫一個(gè)簡(jiǎn)易的lombok,需要的可以參考一下

1.概述

在面向?qū)ο缶幊讨?,必不可少的需要在代碼中定義對(duì)象模型,而在基于Java的業(yè)務(wù)平臺(tái)開發(fā)實(shí)踐中尤其如此。相信大家在平時(shí)開發(fā)中也深有感觸,本來是沒有多少代碼開發(fā)量的,但是因?yàn)槎x的業(yè)務(wù)模型對(duì)象比較多,而需要重復(fù)寫Getter/Setter、構(gòu)造器方法、字符串輸出的ToString方法、Equals/HashCode方法等。我們都知道Lombok能夠替大家完成這些繁瑣的操作,但是其背后的原理很少有人會(huì)關(guān)注或者說得清,本文會(huì)帶著大家了解這一開發(fā)神器內(nèi)部的運(yùn)行機(jī)制與原理!

Lombok是一款Java開發(fā)插件,使得Java開發(fā)者可以通過其定義的一系列注解來消除業(yè)務(wù)工程中冗長(zhǎng)和繁瑣的代碼,尤其對(duì)于簡(jiǎn)單的Java模型對(duì)象(POJO)。在開發(fā)環(huán)境中使用Lombok插件后,Java開發(fā)人員可以節(jié)省出重復(fù)構(gòu)建,諸如HashCode和Equals這樣的方法以及各種業(yè)務(wù)對(duì)象模型的accessor和ToString等方法的大量時(shí)間。對(duì)于這些方法,它能夠在編譯源代碼期間自動(dòng)幫我們生成這些方法,且并不會(huì)如反射那樣降低程序的性能。主要是這樣比較靈活,即使你在實(shí)體類中新增了屬性,也不用重新回過頭來維護(hù)該實(shí)體的set和get方法等。

2.lombok使用方法

安裝插件,在編譯類路徑中加入lombok.jar包(具體安裝方法可自己百度);

在需要簡(jiǎn)化的類或方法上,加上要使用的注解;

使用支持lombok的編譯工具編譯源代碼(關(guān)于支持lombok的編譯工具,見4.支持lombok的編譯工具);

編譯得到的字節(jié)碼文件中自動(dòng)生成Lombok注解對(duì)應(yīng)的方法或代碼;

3.lombok原理解析

接下來,我們進(jìn)行l(wèi)ombok的原理分析,以O(shè)racle的javac編譯工具為例。自Java 6起,javac開始支持JSR 269 Pluggable Annotation Processing API規(guī)范,只要程序?qū)崿F(xiàn)了該API,就能在java源碼編譯時(shí)調(diào)用定義的注解。舉例來說,現(xiàn)在有一個(gè)實(shí)現(xiàn)了"JSR 269 API"的程序A,那么使用javac編譯源碼的時(shí)候具體流程如下:

javac對(duì)源代碼進(jìn)行分析,生成一棵抽象語法樹(AST);

運(yùn)行過程中調(diào)用實(shí)現(xiàn)了"JSR 269 API"的A程序;

此時(shí)A程序就可以完成它自己的邏輯,包括修改第一步驟得到的抽象語法樹(AST);

javac使用修改后的抽象語法樹(AST)生成字節(jié)碼文件;

詳細(xì)的流程圖如下:

從上面的Lombok執(zhí)行的流程圖中可以看出,在Javac 解析成AST抽象語法樹之后, Lombok 根據(jù)自己編寫的注解處理器,動(dòng)態(tài)地修改 AST,增加新的節(jié)點(diǎn)(即Lombok自定義注解所需要生成的代碼),最終通過分析生成JVM可執(zhí)行的字節(jié)碼Class文件。使用Annotation Processing自定義注解是在編譯階段進(jìn)行修改,而jdk的反射技術(shù)是在運(yùn)行時(shí)動(dòng)態(tài)修改,兩者相比,反射雖然更加靈活一些但是帶來的性能損耗更加大。

Lombok本質(zhì)上就是一個(gè)實(shí)現(xiàn)了JSR 269 API的程序,在使用javac的命令過程中,它生效的具體流程如下:

  • javac對(duì)源代碼進(jìn)行分析,生成一棵抽象語法樹(AST);
  • 運(yùn)行過程中調(diào)用實(shí)現(xiàn)了JSR 269 API的lombok程序;
  • 編譯機(jī)會(huì)調(diào)用lombok程序?qū)Φ谝徊降玫降腁ST進(jìn)行處理,找到其注解所在類對(duì)應(yīng)的語法樹(AST),然后修改該語法樹,增加注解對(duì)應(yīng)的方法或代碼片段到定義的相應(yīng)樹節(jié)點(diǎn);
  • javac使用修改后的抽象語法樹生成最終的java字節(jié)碼文件;

4.手寫簡(jiǎn)易lombok

使用的是idea工具進(jìn)行開發(fā),使用的jdk版本為1.8,因?yàn)槲覀兪亲约菏謱懙膇dea提示會(huì)報(bào)錯(cuò),但是能正常運(yùn)行,因?yàn)閘ombok是idea針對(duì)于他有插件提示,我們的沒有,但是也不影響正常使用。

1.我們需要使用到j(luò)dk安裝路徑下lib包下的tools.jar,我們可以收到加入到項(xiàng)目依賴,也可以在maven中直接引入。我們直接使用idea新建一個(gè)普通的maven項(xiàng)目,然后配置如下,最后將這個(gè)項(xiàng)目打包一下,在別的項(xiàng)目中引入即可。

maven配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>lombok</groupId>
    <artifactId>com.compass.lombok</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8</version>
            <scope>system</scope>
            <systemPath>C:/Program Files/Java/jdk1.8.0_251/lib/tools.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>com.google.auto.service</groupId>
            <artifactId>auto-service</artifactId>
            <version>1.0-rc5</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

還有一個(gè)是 com.google.auto.service 這個(gè)是使用SPI機(jī)制的一個(gè)依賴,關(guān)于spi可以自行百度了解,這里就不再進(jìn)行展開。

關(guān)鍵核心接口:AbstractProcessor,這個(gè)就是在編譯期處理注解的一個(gè)接口,然后我們可以通過實(shí)現(xiàn)這個(gè)接口通過修改字節(jié)碼文件,最終在字節(jié)碼文件中生成get和set方法。

首先我們定義一個(gè)DATA注解:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Data {
}

然后寫一個(gè) DataAnnotationProcessor 繼承AbstractProcessor即可

import com.compass.lombok.annotation.Data;
import com.google.auto.service.AutoService;
import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;

/**
 * @author compass
 * @date 2022-10-13
 * @since 1.0
 **/
@AutoService(Processor.class)
@SupportedAnnotationTypes("com.compass.lombok.annotation.Data")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DataAnnotationProcessor extends AbstractProcessor {
    private JavacTrees javacTrees;
    private TreeMaker treeMaker;
    private Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
         javacTrees = JavacTrees.instance(context);
         treeMaker = TreeMaker.instance(context);
         names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Data.class);
        for (Element element : set) {
            javacTrees.getTree(element).accept(new TreeTranslator(){
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    jcClassDecl.defs.stream()
                            .filter(it->it.getKind().equals(Tree.Kind.VARIABLE))
                            .map(it->(JCTree.JCVariableDecl) it).forEach(it->{
                                jcClassDecl.defs = jcClassDecl.defs.prepend(genGetterMethod(it));
                                jcClassDecl.defs = jcClassDecl.defs.prepend(genSetterMethod(it));

                    });
                    super.visitClassDef(jcClassDecl);
                }
            });
        }
        return true;
    }

    private JCTree.JCMethodDecl genGetterMethod(JCTree.JCVariableDecl jcVariableDecl){
        JCTree.JCIdent _this = treeMaker.Ident(names.fromString("this"));
        Name name = jcVariableDecl.getName();
        JCTree.JCFieldAccess select = treeMaker.Select(_this, name);
        JCTree.JCReturn returnStatement = treeMaker.Return(select);

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        statements.append(returnStatement);

        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);

        Name getMethodName = getGetMethodName(jcVariableDecl.getName());

        JCTree.JCExpression returnMethodType = jcVariableDecl.vartype;

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        List<JCTree.JCTypeParameter> methodGenericParamList = List.nil();

        List<JCTree.JCVariableDecl> parameterList = List.nil();

        List<JCTree.JCExpression> throwList = List.nil();

        return treeMaker.MethodDef(modifiers, getMethodName, returnMethodType, methodGenericParamList, parameterList, throwList, body, null);

    }
    public  JCTree.JCMethodDecl genSetterMethod(JCTree.JCVariableDecl jcVariableDecl){

        JCTree.JCIdent _this = treeMaker.Ident(names.fromString("this"));
        Name name = jcVariableDecl.getName();
        JCTree.JCFieldAccess select = treeMaker.Select(_this, name);
        JCTree.JCAssign statementAssign = treeMaker.Assign(select, treeMaker.Ident(jcVariableDecl.getName()));
        JCTree.JCExpressionStatement statement = treeMaker.Exec(statementAssign);
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        statements.append(statement);

        JCTree.JCVariableDecl params = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER, List.nil()),
                jcVariableDecl.name,
                jcVariableDecl.vartype,
                null
        );

        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);

        Name setMethodName = getSetMethodName(jcVariableDecl.getName());

        JCTree.JCExpression returnMethodType = treeMaker.Type(new Type.JCVoidType());

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        List<JCTree.JCTypeParameter> methodGenericParamList = List.nil();

        List<JCTree.JCVariableDecl> parameterList = List.of(params);

        List<JCTree.JCExpression> throwList = List.nil();

        return treeMaker.MethodDef(modifiers, setMethodName, returnMethodType, methodGenericParamList, parameterList, throwList, body, null);
    }

    private Name getGetMethodName(Name name){
        String filedName = name.toString();
        return names.fromString("get"+filedName.substring(0,1).toUpperCase()+filedName.substring(1));
    }

    private Name getSetMethodName(Name name){
        String filedName = name.toString();
        return names.fromString("set"+filedName.substring(0,1).toUpperCase()+filedName.substring(1));
    }

}

其實(shí)到這里就編寫完畢了,這里去動(dòng)態(tài)修改字節(jié)碼,然后生成了get和set方法,至于其他的方法那就后面再說,此案例參照于《深入jvm字節(jié)碼》進(jìn)行編寫。

最后在maven項(xiàng)目中打包

在別的項(xiàng)目直接使用即可,直接在別的項(xiàng)目的實(shí)體類上加上@Data注解即可生成get和set方法,但是沒有方法提升,但是能正常運(yùn)行,這里是idea的一個(gè)代碼提示的問題,因?yàn)槲覀冞@個(gè)沒有對(duì)應(yīng)的idea插件,所以idea會(huì)提示報(bào)錯(cuò),但是能正常運(yùn)行。

到此這篇關(guān)于利用Java手寫一個(gè)簡(jiǎn)易的lombok的示例代碼的文章就介紹到這了,更多相關(guān)Java手寫lombok內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Spring MVC的攔截器與異常處理機(jī)制

    詳解Spring MVC的攔截器與異常處理機(jī)制

    這篇文章主要為大家詳細(xì)介紹了Spring MVC的攔截器與異常處理機(jī)制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • Mybatis全面分頁插件

    Mybatis全面分頁插件

    這篇文章主要為大家詳細(xì)介紹了Mybatis全面分頁插件的使用方法,比較適用于在分頁時(shí)候進(jìn)行攔截,感興趣的小伙伴們可以參考一下
    2016-08-08
  • SpringBoot實(shí)現(xiàn)圖形驗(yàn)證碼的操作方法

    SpringBoot實(shí)現(xiàn)圖形驗(yàn)證碼的操作方法

    隨著安全性的要求越來越高,目前許多項(xiàng)目中都使用了驗(yàn)證碼,驗(yàn)證碼也有各種類型,如 圖形驗(yàn)證碼、短信驗(yàn)證碼、郵件驗(yàn)證碼、人臉識(shí)別等,本文給大家介紹SpringBoot實(shí)現(xiàn)圖形驗(yàn)證碼的方法,感興趣的朋友跟隨小編一起看看吧
    2024-07-07
  • Java(SpringBoot)項(xiàng)目打包(構(gòu)建)成Docker鏡像的幾種常見方式

    Java(SpringBoot)項(xiàng)目打包(構(gòu)建)成Docker鏡像的幾種常見方式

    在對(duì)Spring Boot應(yīng)用程序進(jìn)行Docker化時(shí),為應(yīng)用程序選擇正確的基礎(chǔ)鏡像非常重要,下面這篇文章主要給大家介紹了關(guān)于Java(SpringBoot)項(xiàng)目打包(構(gòu)建)成Docker鏡像的幾種常見方式,需要的朋友可以參考下
    2023-12-12
  • 為什么說要慎用SpringBoot @ComponentScan

    為什么說要慎用SpringBoot @ComponentScan

    本文主要介紹了為什么說要慎用SpringBoot @ComponentScan,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • 基于Java反射的map自動(dòng)裝配JavaBean工具類設(shè)計(jì)示例代碼

    基于Java反射的map自動(dòng)裝配JavaBean工具類設(shè)計(jì)示例代碼

    這篇文章主要給大家介紹了關(guān)于基于Java反射的map自動(dòng)裝配JavaBean工具類設(shè)計(jì)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧
    2018-10-10
  • SSH框架網(wǎng)上商城項(xiàng)目第12戰(zhàn)之添加和更新商品功能

    SSH框架網(wǎng)上商城項(xiàng)目第12戰(zhàn)之添加和更新商品功能

    這篇文章主要介紹了SSH框架網(wǎng)上商城項(xiàng)目第12戰(zhàn)之添加和更新商品功能的實(shí)現(xiàn)代碼,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Java中的線程生命周期核心概念

    Java中的線程生命周期核心概念

    這篇文章主要介紹了Java中的線程生命周期核心概念,通過使用一個(gè)快速的圖解展開文章內(nèi)容,需要的小伙伴可以參考一下
    2022-06-06
  • Springboot靜態(tài)資源訪問實(shí)現(xiàn)代碼解析

    Springboot靜態(tài)資源訪問實(shí)現(xiàn)代碼解析

    這篇文章主要介紹了Springboot靜態(tài)資源訪問實(shí)現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • Java RocketMQ 路由注冊(cè)與刪除的實(shí)現(xiàn)

    Java RocketMQ 路由注冊(cè)與刪除的實(shí)現(xiàn)

    這篇文章主要介紹了Java RocketMQ 路由注冊(cè)與刪除的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評(píng)論