溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Lombok 原理分析與功能實(shí)現(xiàn)

發(fā)布時(shí)間:2020-08-05 15:32:06 來(lái)源:網(wǎng)絡(luò) 閱讀:2441 作者:Java月亮呀 欄目:編程語(yǔ)言

前言

這兩天沒(méi)什么重要的事情做,但是想著還要春招總覺(jué)得得學(xué)點(diǎn)什么才行,正巧想起來(lái)前幾次面試的時(shí)候面試官總喜歡問(wèn)一些框架的底層實(shí)現(xiàn),但是我學(xué)東西比較傾向于用到啥學(xué)啥,因此在這些方面吃了很大的虧。而且其實(shí)很多框架也多而雜,代碼起來(lái)費(fèi)勁,無(wú)非就是幾套設(shè)計(jì)模式套一套,用到的東西其實(shí)也就那么些,感覺(jué)沒(méi)啥新意。剛這兩天讀”深入理解JVM”的時(shí)候突然想起來(lái)有個(gè)叫Lombok的東西以前一直不能理解他的實(shí)現(xiàn)原理,現(xiàn)在正好趁著閑暇的時(shí)間研究研究。

Lombok

代碼

Lombok是一個(gè)開(kāi)源項(xiàng)目,源代碼托管在GITHUB/rzwitserloot,如果需要在maven里引用,只需要添加下依賴:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.8</version>
</dependency>

功能

那么Lombok是做什么的呢?其實(shí)很簡(jiǎn)單,一個(gè)最簡(jiǎn)單的例子就是它能夠?qū)崿F(xiàn)通過(guò)添加注解,能夠自動(dòng)生成一些方法。比如這樣的類:

@Getter
class Test{
    private String value;
}

我們用Lombok提供的@Getter來(lái)注解這個(gè)類,這個(gè)類在編譯的時(shí)候就會(huì)變成:

class Test{
    private String value;
    public String getValue(){
        return this.value;
    }
}

當(dāng)然Lombok也提供了很多其他的注解,這只是其中一個(gè)最典型的例子。其他的用法網(wǎng)上的資料已經(jīng)很多了,這里就不啰嗦。
看上去是很方便的一個(gè)功能,尤其是在很多項(xiàng)目里有很多bean,每次都要手寫(xiě)或自動(dòng)生成setter getter方法,搞得代碼很長(zhǎng)而且沒(méi)有啥意義,因此這個(gè)對(duì)簡(jiǎn)化代碼的強(qiáng)迫癥們還是很有吸引力的。
但是,我們發(fā)現(xiàn)這個(gè)包跟一般的包有很大區(qū)別,絕大多數(shù)java包都工作在運(yùn)行時(shí),比如spring提供的那種注解,通過(guò)在運(yùn)行時(shí)用反射來(lái)實(shí)現(xiàn)業(yè)務(wù)邏輯。Lombok這個(gè)東西工作卻在編譯期,在運(yùn)行時(shí)是無(wú)法通過(guò)反射獲取到這個(gè)注解的。
而且由于他相當(dāng)于是在編譯期對(duì)代碼進(jìn)行了修改,因此從直觀上看,源代碼甚至是語(yǔ)法有問(wèn)題的。
一個(gè)更直接的體現(xiàn)就是,普通的包在引用之后一般的IDE都能夠自動(dòng)識(shí)別語(yǔ)法,但是Lombok的這些注解,一般的IDE都無(wú)法自動(dòng)識(shí)別,比如我們上面的Test類,如果我們?cè)谄渌胤竭@么調(diào)用了一下:

Test test=new Test();
test.getValue();

IDE的自動(dòng)語(yǔ)法檢查就會(huì)報(bào)錯(cuò),說(shuō)找不到這個(gè)getValue方法。因此如果要使用Lombok的話還需要配合安裝相應(yīng)的插件,防止IDE的自動(dòng)檢查報(bào)錯(cuò)。
因此,可以說(shuō)這個(gè)東西的設(shè)計(jì)初衷比較美好,但是用起來(lái)比較麻煩,而且破壞了代碼的完整性,很多項(xiàng)目組(包括我自己)都不高興用。但是他的實(shí)現(xiàn)原理卻還是比較好玩的,隨便搜了搜發(fā)現(xiàn)網(wǎng)上最多也只提到了他修改了抽象語(yǔ)法樹(shù),雖說(shuō)從感性上可以理解,但是還是想自己手敲一敲真正去實(shí)現(xiàn)一下。

原理

翻了翻現(xiàn)有的資料,再加上自己的一些猜想,Lombok的基本流程應(yīng)該基本是這樣:

  • 定義編譯期的注解
  • 利用JSR269 api(Pluggable Annotation Processing API )創(chuàng)建編譯期的注解處理器
  • 利用tools.jar的javac api處理AST(抽象語(yǔ)法樹(shù))
  • 將功能注冊(cè)進(jìn)jar包

看起來(lái)還是比較簡(jiǎn)單的,但是不得不說(shuō)坑也不少,搞了兩天才把流程搞通。。。
下面就根據(jù)這個(gè)流程自己實(shí)現(xiàn)一個(gè)有類似功能的Getter類。

手?jǐn)]Getter

實(shí)驗(yàn)的目的是自定義一個(gè)針對(duì)類的Getter注解,它能夠讀取該類的成員方法并自動(dòng)生成getter方法。

項(xiàng)目依賴

由于比較習(xí)慣用maven,我這里就用maven構(gòu)建一下項(xiàng)目,修改下當(dāng)前的pom.xml文件如下:

<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>com.mythsman.test</groupId>
    <artifactId>getter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>test</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8</version>
            <scope>system</scope>
            <systemPath>${java.home}/../lib/tools.jar</systemPath>
        </dependency>

    </dependencies>

    <build>
        <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>
        </plugins>
    </build>
</project>

主要定義了下項(xiàng)目名,除了默認(rèn)依賴的junit之外(其實(shí)并沒(méi)有用),這里添加了tools.jar包。這個(gè)包實(shí)在jdk的lib下面,因此scope是system,由于${java.home}變量表示的是jre的位置,因此還要根據(jù)這個(gè)位置找到實(shí)際的tools.jar的路徑并寫(xiě)在systemPath里。
由于防止在寫(xiě)代碼的時(shí)候用到j(luò)ava8的一些語(yǔ)法,這里配置了下編譯插件使其支持java8。

創(chuàng)建Getter注解

定義注解Getter.java:

package com.mythsman.test;

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.SOURCE)
public @interface Getter {
}

這里的Target我選擇了ElementType.TYPE表示是對(duì)類的注解,Retention選擇了RententionPolicy.SOURCE,表示這個(gè)注解只在編譯期起作用,在運(yùn)行時(shí)將不存在。這個(gè)比較簡(jiǎn)單,稍微復(fù)雜點(diǎn)的是對(duì)這個(gè)注解的處理機(jī)制。像spring那種注解是通過(guò)反射來(lái)獲得注解對(duì)應(yīng)的元素并實(shí)現(xiàn)業(yè)務(wù)邏輯,但是我們顯然不希望在使用Lombok這種功能的時(shí)候還要編寫(xiě)其他的調(diào)用代碼,況且用反射也獲取不到編譯期才存在的注解。
幸運(yùn)的是Java早已支持了JSR269的規(guī)范,允許在編譯時(shí)指定一個(gè)processor類來(lái)對(duì)編譯階段的注解進(jìn)行干預(yù),下面就來(lái)解決下這個(gè)處理器。

創(chuàng)建Getter注解的處理器

基本框架

自定義的處理器需要繼承AbstractProcessor這個(gè)類,基本的框架大體應(yīng)當(dāng)如下:

package com.mythsman.test;

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

@SupportedAnnotationTypes("com.mythsman.test.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return true;
    }
}

需要定義兩個(gè)注解,一個(gè)表示該處理器需要處理的注解,另外一個(gè)表示該處理器支持的源碼版本。然后需要著重實(shí)現(xiàn)兩個(gè)方法,init跟process。init的主要用途是通過(guò)ProcessingEnvironment來(lái)獲取編譯階段的一些環(huán)境信息;process主要是實(shí)現(xiàn)具體邏輯的地方,也就是對(duì)AST進(jìn)行處理的地方。

具體怎么做呢?

INIT方法

首先我們要重寫(xiě)下init方法,從環(huán)境里提取一些關(guān)鍵的類:

private Messager messager;
private JavacTrees trees;
private TreeMaker treeMaker;
private Names names;

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

我們提取了四個(gè)主要的類:

  • Messager主要是用來(lái)在編譯期打log用的
  • JavacTrees提供了待處理的抽象語(yǔ)法樹(shù)
  • TreeMaker封裝了創(chuàng)建AST節(jié)點(diǎn)的一些方法
  • Names提供了創(chuàng)建標(biāo)識(shí)符的方法
PROCESS方法

process方法的邏輯比較簡(jiǎn)單,但是由于這里的api對(duì)于我們來(lái)說(shuō)比較陌生,因此寫(xiě)起來(lái)還是費(fèi)了不少勁的:

@Override
public synchronized boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);
    set.forEach(element -> {
        JCTree jcTree = trees.getTree(element);
        jcTree.accept(new TreeTranslator() {
            @Override
            public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();

                for (JCTree tree : jcClassDecl.defs) {
                    if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                        JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                        jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                    }
                }

                jcVariableDeclList.forEach(jcVariableDecl -> {
                    messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");
                    jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                });
                super.visitClassDef(jcClassDecl);
            }

        });
    });

    return true;
}

步驟大概是下面這樣:

  1. 利用roundEnv的getElementsAnnotatedWith方法過(guò)濾出被Getter這個(gè)注解標(biāo)記的類,并存入set
  2. 遍歷這個(gè)set里的每一個(gè)元素,并生成jCTree這個(gè)語(yǔ)法樹(shù)
  3. 創(chuàng)建一個(gè)TreeTranslator,并重寫(xiě)其中的visitClassDef方法,這個(gè)方法處理遍歷語(yǔ)法樹(shù)得到的類定義部分jcClassDecl
    1. 創(chuàng)建一個(gè)jcVariableDeclList保存類的成員變量
    2. 遍歷jcTree的所有成員(包括成員變量和成員函數(shù)和構(gòu)造函數(shù)),過(guò)濾出其中的成員變量,并添加進(jìn)jcVariableDeclList
    3. 將jcVariableDeclList的所有變量轉(zhuǎn)換成需要添加的getter方法,并添加進(jìn)jcClassDecl的成員中
    4. 調(diào)用默認(rèn)的遍歷方法遍歷處理后的jcClassDecl
  4. 利用上面的TreeTranslator去處理jcTree

接下來(lái)再實(shí)現(xiàn)makeGetterMethodDecl方法:

private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {

 ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
 statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
 JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
 return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), body, null);
}

private Name getNewMethodName(Name name) {
 String s = name.toString();
 return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
}
</pre>

邏輯就是讀取變量的定義,并創(chuàng)建對(duì)應(yīng)的Getter方法,并試圖用駝峰命名法。

整體上難點(diǎn)還是集中在api的使用上,還有一些細(xì)微的注意點(diǎn):
首先,messager的printMessage方法在打印log的時(shí)候會(huì)自動(dòng)過(guò)濾重復(fù)的log信息。
其次,這里的list并不是java.util里面的list,而是一個(gè)自定義的list,這個(gè)list的用法比較坑爹,他采用的是這樣的方式:

package com.sun.tools.javac.util;

public class List<A> extends AbstractCollection<A> implements java.util.List<A> {
    public A head;
    public List<A> tail;

    //...

    List(A var1, List<A> var2) {
        this.tail = var2;
        this.head = var1;
    }

    public List<A> prepend(A var1) {
        return new List(var1, this);
    }

    public static <A> List<A> of(A var0) {
        return new List(var0, nil());
    }

    public List<A> append(A var1) {
        return of(var1).prependList(this);
    }

    public static <A> List<A> nil() {
        return EMPTY_LIST;
    }
    //...
}

挺有趣的,用這種叫cons而不是list的數(shù)據(jù)結(jié)構(gòu),添加元素的時(shí)候就把自己賦給自己的tail,新來(lái)的元素放進(jìn)head。不過(guò)需要注意的是這個(gè)東西不支持鏈?zhǔn)秸{(diào)用,prepend之后還要將新值賦給自己。
而且這里在創(chuàng)建getter方法的時(shí)候還要把參數(shù)寫(xiě)全寫(xiě)對(duì)了,尤其是添加this指針的這種用法。

測(cè)試類

上面基本就是所有功能代碼了,接下來(lái)我們要寫(xiě)一個(gè)類來(lái)測(cè)試一下(App.java):

package com.mythsman.test;

@Getter
public class App {
    private String value;

    private String value2;

    public App(String value) {
        this.value = value;
    }

    public static void main(String[] args) {
        App app = new App("it works");
        System.out.println(app.getValue());
    }
}

不過(guò),先不要急著構(gòu)建,構(gòu)建了肯定會(huì)失敗,因?yàn)檫@原則上應(yīng)該是兩個(gè)項(xiàng)目。Getter.java是注解類沒(méi)問(wèn)題,但是GetterProcessor.java是處理器,App.java需要在編譯期調(diào)用這個(gè)處理器,因此這兩個(gè)東西是不能一起編譯的,正確的編譯方法應(yīng)該是類似下面這樣,寫(xiě)成compile.sh腳本就是:

#!/usr/bin/env bash

if [ -d classes ]; then
    rm -rf classes;
fi
mkdir classes

javac -cp $JAVA_HOME/lib/tools.jar com/mythsman/test/Getter* -d classes/

javac -cp classes -d classes -processor com.mythsman.test.GetterProcessor com/mythsman/test/App.java

javap -p classes com/mythsman/test/App.class

java -cp classes com.mythsman.test.App

其實(shí)是五個(gè)步驟:

  1. 創(chuàng)建保存class文件的文件夾
  2. 導(dǎo)入tools.jar,編譯processor并輸出
  3. 編譯App.java,并使用javac的-processor參數(shù)指定編譯階段的處理器GetterProcessor
  4. 用javap顯示編譯后的App.class文件(非必須,方便看結(jié)果)
  5. 執(zhí)行測(cè)試類

好了,進(jìn)入項(xiàng)目的根目錄,當(dāng)前的目錄結(jié)構(gòu)應(yīng)該是這樣的:

.
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   ├── com
│   │   │   │   └── mythsman
│   │   │   │       └── test
│   │   │   │           ├── App.java
│   │   │   │           ├── Getter.java
│   │   │   │           └── GetterProcessor.java
│   │   │   └── compile.sh

調(diào)用compile.sh,輸出如下:

Note: value has been processed
Note: value2 has been processed
Compiled from "App.java"
public class com.mythsman.test.App {
  private java.lang.String value;
  private java.lang.String value2;
  public java.lang.String getValue2();
  public java.lang.String getValue();
  public com.mythsman.test.App(java.lang.String);
  public static void main(java.lang.String[]);
}
it works

Note行就是在GetterProcessor類里通過(guò)messager打印的log,中間的是javap反編譯的結(jié)果,最后一行表示測(cè)試調(diào)用成功。

MAVEN構(gòu)建并打包

上面的測(cè)試部分其實(shí)是為了測(cè)試而測(cè)試,其實(shí)這應(yīng)當(dāng)是兩個(gè)項(xiàng)目,一個(gè)是processor項(xiàng)目,這個(gè)項(xiàng)目應(yīng)當(dāng)被打成一個(gè)jar包,供調(diào)用者使用;另一個(gè)項(xiàng)目是app項(xiàng)目,這個(gè)項(xiàng)目是專門(mén)使用jar包的,他并不希望添加任何額外編譯參數(shù),就跟lombok的用法一樣。
簡(jiǎn)單來(lái)說(shuō),就是我們希望把processor打成一個(gè)包,并且在使用時(shí)不需要添加額外參數(shù)。
那么如何在調(diào)用的時(shí)候不用加參數(shù)呢,其實(shí)我們知道java在編譯的時(shí)候會(huì)去資源文件夾下讀一個(gè)META-INF文件夾,這個(gè)文件夾下面除了MANIFEST.MF文件之外,還可以添加一個(gè)services文件夾,我們可以在這個(gè)文件夾下創(chuàng)建一個(gè)文件,文件名是javax.annotation.processing.Processor,文件內(nèi)容是com.mythsman.test.GetterProcessor。
我們知道m(xù)aven在編譯前會(huì)先拷貝資源文件夾,然后當(dāng)他在編譯時(shí)候發(fā)現(xiàn)了資源文件夾下的META-INF/serivces文件夾時(shí),他就會(huì)讀取里面的文件,并將文件名所代表的接口用文件內(nèi)容表示的類來(lái)實(shí)現(xiàn)。這就相當(dāng)于做了-processor參數(shù)該做的事了。
當(dāng)然這個(gè)文件我們并不希望調(diào)用者去寫(xiě),而是希望在processor項(xiàng)目里集成,調(diào)用的時(shí)候能直接繼承META-INF。

好了,我們先刪除App.java和compile.sh,添加下META-INF文件夾,當(dāng)前目錄結(jié)構(gòu)應(yīng)該是這樣的:

.
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── mythsman
│       │           └── test
│       │               ├── Getter.java
│       │               └── GetterProcessor.java
│       └── resources
│           └── META-INF
│               └── services
│                   └── javax.annotation.processing.Processor

當(dāng)然,我們還不能編譯,因?yàn)閜rocessor項(xiàng)目并不需要把自己添加為processor(況且自己還沒(méi)編譯呢怎么調(diào)用自己)。。。完了,好像死循環(huán)了,自己在編譯的時(shí)候不能添加services文件夾,但是又需要打的包里有services文件夾,這該怎么搞呢?
其實(shí)很簡(jiǎn)單,配置一下maven的插件就行,打開(kāi)pom.xml,在project/build/標(biāo)簽里添加下面的配置:

<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-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>

我們知道m(xù)aven構(gòu)建的第一步就是調(diào)用maven-resources-plugin插件的resources命令,將resources文件夾復(fù)制到target/classes中,那么我們配置一下resources標(biāo)簽,過(guò)濾掉META-INF文件夾,這樣在編譯的時(shí)候就不會(huì)找到services的配置了。然后我們?cè)诖虬?prepare-package生命周期)再利用maven-resources-plugin插件的copy-resources命令把services文件夾重新拷貝過(guò)來(lái)不就好了么。
這樣配置好了,就可以直接執(zhí)行mvn clean install打包提交到本地私服:

myths@pc:~/Desktop/test$ mvn clean install
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building test 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ getter ---
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ getter ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ getter ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/myths/Desktop/test/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ getter ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/myths/Desktop/test/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ getter ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ getter ---
[INFO] No tests to run.
[INFO] 
[INFO] --- maven-resources-plugin:2.6:copy-resources (process-META) @ getter ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ getter ---
[INFO] Building jar: /home/myths/Desktop/test/target/getter-1.0-SNAPSHOT.jar
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ getter ---
[INFO] Installing /home/myths/Desktop/test/target/getter-1.0-SNAPSHOT.jar to /home/myths/.m2/repository/com/mythsman/test/getter/1.0-SNAPSHOT/getter-1.0-SNAPSHOT.jar
[INFO] Installing /home/myths/Desktop/test/pom.xml to /home/myths/.m2/repository/com/mythsman/test/getter/1.0-SNAPSHOT/getter-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.017 s
[INFO] Finished at: 2017-12-19T19:57:04+08:00
[INFO] Final Memory: 16M/201M
[INFO] ------------------------------------------------------------------------

可以看到這里的process-META作用生效。

調(diào)用JAR包測(cè)試

重新創(chuàng)建一個(gè)測(cè)試項(xiàng)目app:

.
├── pom.xml
└── src
    └── main
        └── java
            └── com
                └── mythsman
                    └── test
                        └── App.java

pom.xml:

<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>com.mythsman.test</groupId>
    <artifactId>app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>main</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.mythsman.test</groupId>
            <artifactId>getter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
    <build>
        <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>
        </plugins>
    </build>
</project>

App.java:

package com.mythsman.test;

@Getter
public class App {
    private String value;

    private String value2;

    public App(String value) {
        this.value = value;
    }

    public static void main(String[] args) {
        App app = new App("it works");
        System.out.println(app.getValue());
    }
}

編譯并執(zhí)行:

mvn clean compile && java -cp target/classes com.mythsman.test.App

最后就會(huì)在構(gòu)建成功后打印”it works”。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI