【Java学习】从一个简单的HelloWorld项目中入门maven

创建一个maven项目

这里推荐官方文档:maven官方文档

【注】此篇文章也是笔者学习笔记,如有错误,请见谅。
【注】我把Goal翻译成命令。比如Plugin Goal;
ps:我他喵的写了一大半的文章,一不小心按了x,结果告诉我没同步上?草稿也没有!!!气的我想kill 人!!!!迫于无奈只能重写了。。。

一、编写POM

POM(project Object Model)项目对象模型,它是一个XML文件,这个文件包含了maven创建项目所需要项目配置细节等信息, 比如:项目的基本信息,用于描述项目如何构建、声明项目依赖等等。。。他是Maven项目的核心

这个文件包含了一些默认值,像下面的编写dom没有直接给出,但还是会默认给出,比如:项目所在的文件夹是,target;项目的源代码所在的文件夹是:src/main/java ,测试的代码所在文件夹是src/test/java等等。。。

在maven1中,POM叫做project.xml在maven2中改名成了pom.xml

1.创建工作区

mkdir workspace
cd workspace
mkdir hello-world
执行以上命令,创建两个文件夹

命令和插件都配置在了pom.xml!当执行命令或任务的时候,Maven首先在当前文件夹下寻找POM,它读找到了POM,它读取了POM!它得到了所需要的配置信息!它执行了我们给他们的命令!

上面提到了pom.xml里面存在着默认的配置信息,为什么呢?难不成它直接存储在了硬件之上?肯定不是的,那么还应该有个文件,有着某些特殊的功能,,,这个就是Super POM!!!!

《【Java学习】从一个简单的HelloWorld项目中入门maven》

【知识点】Super POM

Super POM,顾名思义,就是超级POM(项目对象模型),和Java类似,所有的POM都继承至这个POM,它是Maven的默认POM,来吧,看看这个POM里面有什下面是2.1.x的maven 的 Super POM代码

    <project>
      <modelVersion>4.0.0</modelVersion>
      <name>Maven Default Project</name>
     
      <repositories>
        <repository>
          <id>central</id>
          <name>Maven Repository Switchboard</name>
          <layout>default</layout>
          <url>http://repo1.maven.org/maven2</url>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
        </repository>
      </repositories>
     
      <pluginRepositories>
        <pluginRepository>
          <id>central</id>
          <name>Maven Plugin Repository</name>
          <url>http://repo1.maven.org/maven2</url>
          <layout>default</layout>
          <snapshots>
            <enabled>false</enabled>
          </snapshots>
          <releases>
            <updatePolicy>never</updatePolicy>
          </releases>
        </pluginRepository>
      </pluginRepositories>
     
      <build>
        <directory>${project.basedir}/target</directory>
        <outputDirectory>${project.build.directory}/classes</outputDirectory>
        <finalName>${project.artifactId}-${project.version}</finalName>
        <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
        <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
        <!-- TODO: MNG-3731 maven-plugin-tools-api < 2.4.4 expect this to be relative... -->
        <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
        <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
        <resources>
          <resource>
            <directory>${project.basedir}/src/main/resources</directory>
          </resource>
        </resources>
        <testResources>
          <testResource>
            <directory>${project.basedir}/src/test/resources</directory>
          </testResource>
        </testResources>
       <pluginManagement>
           <plugins>
             <plugin>
               <artifactId>maven-antrun-plugin</artifactId>
               <version>1.3</version>
             </plugin>       
             <plugin>
               <artifactId>maven-assembly-plugin</artifactId>
               <version>2.2-beta-2</version>
             </plugin>         
             <plugin>
               <artifactId>maven-clean-plugin</artifactId>
               <version>2.2</version>
             </plugin>
             <plugin>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>2.0.2</version>
             </plugin>
             <plugin>
               <artifactId>maven-dependency-plugin</artifactId>
               <version>2.0</version>
             </plugin>
             <plugin>
               <artifactId>maven-deploy-plugin</artifactId>
               <version>2.4</version>
             </plugin>
             <plugin>
               <artifactId>maven-ear-plugin</artifactId>
               <version>2.3.1</version>
             </plugin>
             <plugin>
               <artifactId>maven-ejb-plugin</artifactId>
               <version>2.1</version>
             </plugin>
             <plugin>
               <artifactId>maven-install-plugin</artifactId>
               <version>2.2</version>
             </plugin>
             <plugin>
               <artifactId>maven-jar-plugin</artifactId>
               <version>2.2</version>
             </plugin>
             <plugin>
               <artifactId>maven-javadoc-plugin</artifactId>
               <version>2.5</version>
             </plugin>
             <plugin>
               <artifactId>maven-plugin-plugin</artifactId>
               <version>2.4.3</version>
             </plugin>
             <plugin>
               <artifactId>maven-rar-plugin</artifactId>
               <version>2.2</version>
             </plugin>        
             <plugin>                
               <artifactId>maven-release-plugin</artifactId>
               <version>2.0-beta-8</version>
             </plugin>
             <plugin>                
               <artifactId>maven-resources-plugin</artifactId>
               <version>2.3</version>
             </plugin>
             <plugin>
               <artifactId>maven-site-plugin</artifactId>
               <version>2.0-beta-7</version>
             </plugin>
             <plugin>
               <artifactId>maven-source-plugin</artifactId>
               <version>2.0.4</version>
             </plugin>         
             <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.4.3</version>
             </plugin>
             <plugin>
               <artifactId>maven-war-plugin</artifactId>
               <version>2.1-alpha-2</version>
             </plugin>
           </plugins>
         </pluginManagement>
      </build>
     
      <reporting>
        <outputDirectory>${project.build.directory}/site</outputDirectory>
      </reporting>
      <profiles>
        <profile>
          <id>release-profile</id>
     
          <activation>
            <property>
              <name>performRelease</name>
              <value>true</value>
            </property>
          </activation>
     
          <build>
            <plugins>
              <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                  <execution>
                    <id>attach-sources</id>
                    <goals>
                      <goal>jar</goal>
                    </goals>
                  </execution>
                </executions>
              </plugin>
              <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <executions>
                  <execution>
                    <id>attach-javadocs</id>
                    <goals>
                      <goal>jar</goal>
                    </goals>
                  </execution>
                </executions>
              </plugin>
              <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <configuration>
                  <updateReleaseInfo>true</updateReleaseInfo>
                </configuration>
              </plugin>
            </plugins>
          </build>
        </profile>
      </profiles>
     
    </project>

在里面我们真的看到了一些文件夹的配置信息,猜想正确。里面还包含了一些仓库地址,还有一些profiles的内容等等,暂时先不去管太多。

【知识点】Minimal POM

Minimal POM不是与Super POM对应的,应该是对于一个POM而言,需要提供的最基本的元素有哪些。那么来看一下最基本的元素吧。

  • project根元素
  • modelVersion-应该被设置成4.0.0(maven2和maven3都是这样)
  • groupId-项目组的id
  • artifactId- 在给定组下的artifact的版本

下面是一个例子:

    <project>
      <modelVersion>4.0.0</modelVersion>
      <groupId>com.mycompany.app</groupId>
      <artifactId>my-app</artifactId>
      <version>1</version>
    </project>

groupId和artifactId和version这三个元素的值决定项目中完整的有效artifact name。格式如下:
<groupId>:<artifactId>:<version>,上面代码的完整有效的artifact name 就是"com.mycompany.app:my-app:1"
如果没有指定的就会是默认的打包类型。

【注】在软件工程里面:所有软件开发过程中的有形产物都可以叫artifact比如开发过程中,源代码,文档,编译后的文件,可执行文件等等。测试过程中,测试用例,测试文档,测试结果等等此概念可大可小,由所参与的软件工程活动决定其范围。在不同的阶段,不同的场景可能都不一样

【疑】或许我们可以把它叫做构件?

【疑】Minimal POM,关于Minimal POM的概念还不是很清楚,他到底是不是除Super POM之外的统称呢?还是如我理解的这样呢?

2.创建pom.xml文件

回到刚才创建的pom.xml文件。输入一下命令。
sudo vim ./hello-world/pom.xml

书写以下代码:

<?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.ort/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.mycom.app</groupId>
        <artifactId>hello-world</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>Maven Hello World Project</name>
</project>

可以看出,第一行是XML头,project是POM的根元素。而之后都是就是一些配置的元素。
看一下第一个子元素:
modelVersion:当前的POM模型的版本。对于maven2和maven3来说他只能是4.0.0
groupId:组的Id,表示此项目属于哪个组,那个组的Id是什么?例如:如果你的公司是mycom,有一个项目为myapp,那么组的id就是:com.mycom.myapp
artifactId:这个是当前Maven项目在组中唯一的id,也就是这个id要体现他自己的功能,并且要独一无二。因为一个大型的项目会有很多功能模块,所以会有不同的artifactId。
version:就是当前项目的版本,值得注意的是这里的版本是1.0-SNAPSHOT,意为1.0快照,表示当前版本处在开发中,不是稳定的版本。
name:不是必须的,只是对于用户友好的一个项目名称。

二、编写主代码

项目的主代码所在的文件夹是:src/main/java
项目的测试代码位于:src/test/java
所以要依次创建文件夹。
然后书写代码如下:

package com.mycom.app.helloworld;

public class HelloWorld{
    public String sayHello(){
        return "Hello World";
    }

    public static void main(String[] args){
        System.out.println(new HelloWorld().sayHello());
    }
}

我们重新梳理一下。看一下目录的结构

《【Java学习】从一个简单的HelloWorld项目中入门maven》

【注】之前的图有错误,文件结构是hello-world,而不是写的helloworld,现在在想要不要更改,java文件中包的地址呢当我把Helloworld中的包地址,修改为hello-world时出现错误,因为不允许-出现,后来我又把package直接删掉,依然能够运行。还是不太理解这里。

这些个目录就是groupId和artifactId共同决定的!

该Java类的包名是com.mycom.app.helloworld

这时候,在项目根目录下,即与pom.xml相同的文件夹下面执行:mvn clean compile

clean告诉Maven清理输出目录target/ ,compile告诉Maven编译主代码。

运行后的目录结构

《【Java学习】从一个简单的HelloWorld项目中入门maven》

【知识点】生命周期

刚才的命令,看的是云里雾里,了解下生命周期吧。
什么是生命周期呢?人的生命周期有哪些?—出生 满月 少年 青年 中年 老年 死亡

这就是生命周期,Maven是以构建生命周期为核心的理念。意思就是说从构建到发布一个artifact(project)得到了清晰地定义。

意思就是现在你仅仅需要学会一系列命令就可以轻松的创建任何一个Maven项目,其他的工作都交给POM去办,POM会确保他们都得到相应的结果。

Maven中有三种内嵌的生命周期,default clean site,default生命周期处理项目的部署,clean生命周期处理项目的清理,site生命周期处理项目站点文档的创建。

构建生命周期由以下阶段组成
下面的列表包括了default clean site`生命周期的的执行顺序。直接截取官方文档。点击直接到达

《【Java学习】从一个简单的HelloWorld项目中入门maven》

《【Java学习】从一个简单的HelloWorld项目中入门maven》

三、编写测试代码

在之前我们已经知道了,测试代码和主代码不在一起,这样的好处是是项目的结构更加清晰。
在编写测用例之前,所以我们需要继续创建文件夹:src/test/java
而实际上,要进行单元测试,需要依赖一个JUnit的构件,所以我们要为Hello World添加一个JUnit依赖。在POM中添加如下?

        <dependencies>
                <dependency>
                        <groupId>junit</groupId>
                        <artifactId>junit</artifactId>
                        <version>4.7</version>
                        <scope>test</scope>
                </dependency>
        </dependencies>

由上面可以知道,所有需要的依赖,都放在了dependencies元素的下面,同时依赖里相关的配置都需要在放在dependency元素里面。
我们也都知道,通过groupId和artifactId可以找到这个构件,但是这个貌似有一点不同,因为他的groupId只有一个Junit!!在本地的仓库里面也没有找到!好吧,其实是有一个中央仓库。

【知识点】仓库

何为Maven仓库?

  1. 仓库中存在构建项目的组件和各种各样的依赖。
  2. 构件完成后生成的构件也可以安装或部署到仓库中,用install命令。

仓库严格的分为两种类型,一种是本地一种是远程。
本地仓库
本地仓库指的是从远程仓库下载的安装在你自己本地的仓库

本地仓库的位置是可以改变的,修改maven安装目录下的conf文件夹下的setting.xml文件,即可

【注】有的人建议把这个文件复制一份放在本地的仓库中,这样的好处,就是只有当前用户,是这个配置,此电脑的其他用户,在使用的试试,本地仓库就是他们自己设置的地址,或者是默认的。

将本地项目的构件安装到maven仓库中,如何去做呢?
项目根目录中,运行mvn clean install

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Hello World Project 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world ---
[INFO] Deleting /home/dreamer/workspace/hello-world/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/dreamer/workspace/hello-world/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /home/dreamer/workspace/hello-world/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/dreamer/workspace/hello-world/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] No tests to run.
[INFO] 
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-world ---
[INFO] Building jar: /home/dreamer/workspace/hello-world/target/hello-world-1.0-SNAPSHOT.jar
[INFO] 
[INFO] --- maven-install-plugin:2.4:install (default-install) @ hello-world ---
[INFO] Installing /home/dreamer/workspace/hello-world/target/hello-world-1.0-SNAPSHOT.jar to /home/dreamer/document/maven_local/com/mycom/app/hello-world/1.0-SNAPSHOT/hello-world-1.0-SNAPSHOT.jar
[INFO] Installing /home/dreamer/workspace/hello-world/pom.xml to /home/dreamer/document/maven_local/com/mycom/app/hello-world/1.0-SNAPSHOT/hello-world-1.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.620 s
[INFO] Finished at: 2018-03-09T15:52:32+08:00
[INFO] Final Memory: 16M/203M
[INFO] ------------------------------------------------------------------------

在本地仓库中,真的找到了!

《【Java学习】从一个简单的HelloWorld项目中入门maven》

不妨我们更加深入探讨一下,为什么它的输出目录就是本地仓库下的com/mycom/app 为什么它的名字是这种格式和jar呢?
从源码出发,看一下他到此经历了什么。

        private static final char PATH_SEPARATOR = '/';  
      
        private static final char GROUP_SEPARATOR = '.';  
      
        private static final char ARTIFACT_SEPARATOR = '-';  
      
        public String pathOf( Artifact artifact )  
        {  
            ArtifactHandler artifactHandler = artifact.getArtifactHandler();  
      
            StringBuilder path = new StringBuilder( 128 );  
      
            path.append( formatAsDirectory( artifact.getGroupId() ) ).append( PATH_SEPARATOR );  
            path.append( artifact.getArtifactId() ).append( PATH_SEPARATOR );  
            path.append( artifact.getBaseVersion() ).append( PATH_SEPARATOR );  
            path.append( artifact.getArtifactId() ).append( ARTIFACT_SEPARATOR ).append( artifact.getVersion() );  
      
            if ( artifact.hasClassifier() )  
            {  
                path.append( ARTIFACT_SEPARATOR ).append( artifact.getClassifier() );  
            }  
      
            if ( artifactHandler.getExtension() != null && artifactHandler.getExtension().length() > 0 )  
            {  
                path.append( GROUP_SEPARATOR ).append( artifactHandler.getExtension() );  
            }  
      
            return path.toString();  
    }  
      
        private String formatAsDirectory( String directory )  
        {  
            return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );  
        }  

我们从上面的源码可以看出,一个构件是怎么输出到准确的位置的。

  1. 首先,先将构件的groupId中的.替换成/,同时在后面加了一个/,这时的path=“com/mycom/app/"
  2. 然后得到构件的ArtifactId和BaseVersion,将其添加到了,path的后面。此时path="com/mycom/app/hello-world/1.0-SNAPSHOT/
  3. 再获取一遍artifactId和version,按如下格式拼接artifactId-version-然后再添加到中去,此时的path="com/mycom/app/hello-world/1.0-SNAPSHOT/hello-world-1.0-SNAPSHOT
  4. 检查构件否有classifier,如果有,就继续用-连接。
  5. 检查构件的extension,若extension存在,则加上句点分隔符和extension。可以看到extension是从artifactHandler而给artifact获取,artifactHandler是由项目的packaging决定,也就是packaging决定了构件的扩展名。

【疑】经过以上的源码分析,由于第2步中,获取的是baseVersion。(baseVersion是专门为快照版本准备的,version为1.0-SNAPSHOT的构件,其baseVersion就是1.0),所以应该是目录的结构应该是1.0才对?为何文件夹是1.0-SNAPSHOT?

中央仓库
最原始的本地仓库是空的,Maven至少需要一个可用的远程仓库,远程仓库指的是除本地仓库以外的任何其他的仓库,可以通过file://http://等协议访问。

远程仓库的配置信息,其实在我们安装maven的时候就存在了,不信把M2_HOME/lib/maven-model-builder-3..jar解压,访问org/apache/maven/model/pom-4.0.0.xml就可以看到:

 <repositories>
    <repository>
      <id>central</id>
      <name>Central Repository</name>
      <url>https://repo.maven.apache.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

仔细的分析一下,maven的中央仓库地址发现更改了,(但是之前的http://repo1.maven.org/maven2 也能用),布局是默认的,也就是之前的源码分析,下面的<snapshots> <enabled>元素则是设置成了false,这个意思就是,不会从该仓库下载快照版本的构件。

远程仓库的配置
了解了很多了,但是我们只有一个默认的中央仓库还是不够的,还需要一个远程仓库,这个远程仓库该如何配置呢?肯定还是在项目的pom.xml中设置!继续在pom.xml中添加代码如下:

        <repositories>
                <repository>
                        <id>jboss</id>
                        <name>JBoss Repository</name>
                        <url>http://repository.jboss.com/maven2</url>
                        <releases>
                                <enabled>true</enabled>
                        </releases>
                        <snapshots>
                                <enabled>false</enabled>
                        </snapshots>
                        <layout>default</layout>
                </repository>
        </repositories>

这些素估计你都能看懂吧,其中,releases的enabled值是true,表示开启JBoss仓库的发布版本下载支持。。

<snapshots><release>元素除了enabled,他们还包括另外两个子元素updatePolicy和checksumPolicy

<snapshots>
    <enabled>true</enabled>
    <updatePolicy>daily</updatePolicy>
    <checksumPolicy>ignore</checksumPolicy>
</snapshots>

<updatePolicy>表示的是Maven从远程仓库检查更新的频率,默认值是daily,其他值为never always(每构建都会检查更新) interval:X(每个X分钟检查一次更新。(x为任意证整数)

<checksumPolicy>用来配置Maven检查检验和文件的策略。当构件被部署到Maven仓库的时候,会同时部署对应的校验和文件。如果校验失败,值为warn时,会打印出警告信息,fail时会让构建失败,ignore使得它忽视这个错误。

部署到远程仓库
在一个公司中,大家肯定要共用一些依赖,这些依赖和构建在别的仓库上市不存在的,于是创建了私服,这时出现了一个问题,如何将开发中生成的构件部署到仓库中呢?

还是需要修改项目的pom.xml文件,这时我们应该添加的元素是<distributionManagement>具体代码如下:

<distributionManagement>
    <repository>
        <id>proj-releases</id>
        <name>Proj Release Repository</name>
        <url>http://192.168.1.1000/content/repositories/proj-releases</url>
    </repository>
    <snapshotRepository>
        <id>proj-snapshots</id>
        <name>Proj Snapshot Repository</name>
        <url>http://192.168.1.100/content/repositories/proj-snapshots</url>
    </snapshotRepository>
    
</distributionManagement>

由上面可以见到,包含两个版本,一个是发布版本的构件的仓库,一个是快照版本的仓库。
通过mvn clean deploy来进行部署.

测试代码

呼,终于所有的都弄完了,现在开始写代码吧。在src/test/java下创建文件HelloWorldTest.java代码如下:

package com.mycom.app.helloworld;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class HelloWorldTest{
        @ Test
        public void testSayHello(){
                HelloWorld helloWorld = new HelloWorld();
                String result  = helloWorld.sayHello();
                assertEquals("Hello World",result);

        }

}

【疑】在这里的package 和导入的两个包在哪里呢?为何没有找到?
自我答疑,我们导入了junit,这个包在本地仓库的junit/junit/4.7的jar包中,我觉得这就是maven的好处吧,他把仓库的地方和项目的地方结合在了一起,这样直接导入org.junit.Test 通过pom就可以找到了!真的很神奇!

【疑】在package中,本来是导的hello-world,但是出错,不允许,该怎么办?

关于里面的Junit的内容可以先不理,只看代码理解就可以了。
运行mvn clean test进行测试。
成功了!

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Hello World Project 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world ---
[INFO] Deleting /home/dreamer/workspace/hello-world/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/dreamer/workspace/hello-world/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /home/dreamer/workspace/hello-world/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/dreamer/workspace/hello-world/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /home/dreamer/workspace/hello-world/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] Surefire report directory: /home/dreamer/workspace/hello-world/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.mycom.app.helloworld.HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.076 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.054 s
[INFO] Finished at: 2018-03-09T17:52:45+08:00
[INFO] Final Memory: 16M/199M
[INFO] ------------------------------------------------------------------------

需要注意的是,他先下载了junit-4.7.pom和junit-4.7.jar,上图没有显示是因为我执行了几次,已经下载过了。

四、打包

终于到了这一时刻,将项目编译、测试之后,下一个重要的命令就是打包(package)此项目中没有制定打包类型,前面所说的就是用默认类型jar包。
使用命令mvn clean package
输出如下:

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Hello World Project 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-world ---
[INFO] Deleting /home/dreamer/workspace/hello-world/target
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/dreamer/workspace/hello-world/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /home/dreamer/workspace/hello-world/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-world ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /home/dreamer/workspace/hello-world/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 1 source file to /home/dreamer/workspace/hello-world/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-world ---
[INFO] Surefire report directory: /home/dreamer/workspace/hello-world/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.mycom.app.helloworld.HelloWorldTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.055 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-world ---
[INFO] Building jar: /home/dreamer/workspace/hello-world/target/hello-world-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.098 s
[INFO] Finished at: 2018-03-09T18:05:29+08:00
[INFO] Final Memory: 17M/202M
[INFO] ------------------------------------------------------------------------

现在,我仅仅将项目主代码打包成了hello-world-1.0-SNAPSHOT.jar`,并输出到了/target/文件夹中,如何让其他项目也能用此包呢?
有两种方法,第一种将此jar包放在其他项目的classpath中,(在刚开始设置了classpath有一项就是本目录下,所以将它放在其他项目的目录下就好了)
第二种则是使用mvn clean install就可以直接使用了!
其实使用install命令就会执行前面的所有命令。

五、运行

默认打包生成的jar文件是不能直接运行的,因为带有main方法的累心不会添加到manifect中(打开jar文件中的META-INF/MANIFEST.MF文件,无法看到Main-Class一行)为了生成可执行的jar文件,需要借助maven-shade-plugin

这个是插件!

【知识点】插件

Maven的核心仅仅定义了抽象的生命周期,而插件是用来执行实际的构件任务的。

什么是插件
Maven是一个一系列Maven Plugins(Maven 插件)的核心架构。
换句话说,插件就是那些实际执行的动作,插件可以用来创建jar文件,创建war文件,编译代码,单元测试,创建项目文档,等等,几乎任何项目中你能想到的动作都是maven插件实现的。这个文件夹下面吗?

插件是Maven的核心功能,允许在多个项目中重用通用构建逻辑。 他们通过项目对象模型(POM)执行“动作”(比如创建WAR文件或编译单元测试)来完成此操作。 插件行为可以通过一组独特的参数来定制,这些参数通过每个插件命令(或者Mojo)的描述公开。

其中最简单的插件是Clean Plugin,maven-clean-plugin的工作是一处Maven 项目的目标文件夹。当你运行mvn cleanMaven
执行定义在Clean插件的clean命令,target文件夹就被删除了,Clean插件亦可以通过定义参数来定制插件的行为。outputDrectory就是这个参数,它默认的是$(项目创建所在文件夹)

【疑】我发现插件的文件夹在本地仓库文件夹下面的org/apache/maven/plugins这里就是执行命令的地方,如果我想自定义插件的话,也应该移到这个文件夹下面吗?还有一个有些小不懂得就是,之前我们用install命令讲我们自己所写的类,安装到了本地仓库,那个jar包应该不算插件吧,因为他没有执行命令,只是提供这样一个功能共享而已吧?希望我理解的没错。

什么是Mojo
Mojo就是在Maven中的一个命令(goal),插件有一些命令组成,Mojos能被定义为注释类或者Beanshell脚本,一个Mojo描述了一个命令的元数据:命令的名字 它所适应的生命周期 他的参数。

内置绑定
Maven在和行为一些主要的生命周期阶段绑定了很多插件的命令(goal),当用户通过命令行调用声明后期阶段的时候,对应的插件命令(goal)就会执行相应的任务,也就是说如果要想让这个插件执行一些命令的话,就必须先绑定生命周期,Maven里面还有一些没有绑定的,就什么工作都干不了,这就需要了人工绑定。

自定义绑定
一个例子:如果我要创建项目的源码jar包,我们已经知道maven-source-plugin可以执行jar-no-fork命令,但是没有绑定生命周期,我们现在用不了,所以如何绑定到default生命周期的verify上面呢??
【注】下面的这些我不太理解,只是加上自己的理解。因为我没有找到那个插件,不知道是先下载下来,还是说怎么回事。

进入本地仓库的`org/apache/maven/plugins/maven-source-plugin修改Pom文件。
如下:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>
            <version>2.1.2</verison>
            <executions>
                <execution>
                    <id>attach-sources</id>
                    <phase>verrigy</phase>
                    <goals>
                        <goal>jar-no-fork</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

注意phase参数,把它放在了verify的阶段,输入mvn verify就会成功完成这个任务了。当然如果将这个phase删除,再次执行mvn verify还是会成功,这是因为在很多插件的目标在编写时他们就已经定义了默认绑定阶段,可以使用maven-help-plugin查看插件详细信息。
输入mvn help:describe-Dplugin=org.apache.maven.plugins:maven-sourse-plugin:2.1.1-Ddetail

插件的配置

完成了插件和生命周期的绑定之后,我们还可以配置插件goal的参数,进一步调整插件目标所执行的任务,以满足项目的需要。

  1. 命令行插件配置
    用户可以在Maven命令中使用-D参数,并用=配置参数,如:
    maven-surefire-plugin提供了一个maven.test.skip参数,它值为true的时候,就会跳过执行测试,于是在运行命令的时候,输入如下命令就能跳过测试。
    mvn install-Dmaven.test.skip=true
  2. Pom中全局配置
    并不是所有的插件参数都适合从命令行配置,有些参数的值从项目创建到发布都不会改变,或很少改变,对于这种情况,在pom中一次性配置显然更方便。如:我们通常会需要配置maven-compiler-plugin告诉它变异Java1,5版本的源文件,生成与JVM。5兼容的字节码文件,所以在它的pom中这么配置:
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.1</version>
            <configuration>
                <source>1.5</source>
                <target>1.5</target>
            </configurtion>
        </plugin>
    </plugins>
</build>

所以说Pom是Maven的核心,插件的配置都在里面在<build>参数下面,通过<configuration>可以配置参数值,通过executions下的execution可以配置命令(goal)。当然一个插件可以配置多个命令,从他的复数状态就可以看出。

配置maven-shade-plugin

使它能够生成可执行jar文件。
在pom中修改如下:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>1.2.1</verison>
            <executions>
                <execution> 
                    <phase>paxkage</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation = "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.mycom.app.helloworld.HelloWorld</mainClass>
                            </transformer>
                            
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

【注】我之前理解错误,我一直以为修改插件,配置插件,必须得在本地仓库中配置,然而不是这样的,是在你用到的项目中的POM修改,它会下载你所要用到的插件,换一种方式去理解,插件是别人提供给你的功能,如果你直接修改插件的话,那其他人要用这个插件怎么办?要修改的只需要在本项目中修改就好,不要修改原本的插件。

现在运行mvn clean install就能生成可执行的jar包了,打开这个包看一下里面的MANIFEST.MF 就会发现最后一行多了这个:

《【Java学习】从一个简单的HelloWorld项目中入门maven》

现在在项目的根目录下输入:java-jar target \hello-world-1.0-SNAPSHOT.jar就会输出了:

dreamer@dreamer-Aspire-VN7-591G:~/workspace/hello-world$ java -jar target/hello-world-1.0-SNAPSHOT.jar
Hello World

正是我们所需要的。

六、使用Archetype生成项目骨架

什么是项目骨架?

项目骨架就是我们这个项目的结构,下面的图就是刚才HelloWorld的项目骨架:

.
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── com
│   │           └── mycom
│   │               └── app
│   │                   └── hello-world
│   │                       └── HelloWorld.java
│   └── test
│       └── java
│           └── HelloWorldTest.java
└── target
    ├── classes
    │   └── com
    │       └── mycom
    │           └── app
    │               └── helloworld
    │                   └── HelloWorld.class
    ├── hello-world-1.0-SNAPSHOT.jar
    ├── maven-archiver
    │   └── pom.properties
    ├── maven-status
    │   └── maven-compiler-plugin
    │       ├── compile
    │       │   └── default-compile
    │       │       ├── createdFiles.lst
    │       │       └── inputFiles.lst
    │       └── testCompile
    │           └── default-testCompile
    │               ├── createdFiles.lst
    │               └── inputFiles.lst
    ├── original-hello-world-1.0-SNAPSHOT.jar
    ├── surefire-reports
    │   ├── com.mycom.app.helloworld.HelloWorldTest.txt
    │   └── TEST-com.mycom.app.helloworld.HelloWorldTest.xml
    └── test-classes
        └── com
            └── mycom
                └── app
                    └── helloworld
                        └── HelloWorldTest.class

28 directories, 14 files

想一下,我们怎么做的,先创建src/main/java
z再创建src/test/java…
难不成每次项目都要建这么一大堆东西吗?fuck!!
Maven提供了Archetype来帮助我们快速勾勒出项目的骨架!!

在maven3中,简单的运行mvn archetype:generate就可以轻松解决!!

当然这个也算是一个命令,所以我们实际上是在运行插件maven-archetype-plugin,generate是插件的目标。
快来试一下:

下面我们创建一个HelloMaven的项目。
执行mvn archetype:generate
第一次运行它他会下载各种各样的构件,紧接着它输出:

2096: remote -> uk.ac.rdg.resc:edal-ncwms-based-webapp (-)
2097: remote -> uk.co.nemstix:basic-javaee7-archetype (A basic Java EE7 Maven archetype)
2098: remote -> uk.co.solong:angular-spring-archetype (So Long archetype for RESTful spring services with an AngularJS frontend. Includes debian deployment)
2099: remote -> us.fatehi:schemacrawler-archetype-maven-project (-)
2100: remote -> us.fatehi:schemacrawler-archetype-plugin-command (-)
2101: remote -> us.fatehi:schemacrawler-archetype-plugin-dbconnector (-)
2102: remote -> us.fatehi:schemacrawler-archetype-plugin-lint (-)
2103: remote -> xyz.luan.generator:xyz-generator (-)

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1147://在这里吓坏我,往上面一翻有2103个选择,,该选择哪个数字呢?暂时还不知道,那就默认的吧是1147,直接回车

Choose org.apache.maven.archetypes:maven-archetype-quickstart version: 
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
Choose a number: 6: //按默认的来,直接回车。
...
...//中间下载了一些东西
Define value for property 'groupId': com.mycom.app
Define value for property 'artifactId': hello-maven
Define value for property 'version' 1.0-SNAPSHOT: : //直接回车或者是设置版本
Define value for property 'package' com.mycom.app: ://这个是项目默认的jar包名,直接回车。 
Confirm properties configuration:
groupId: com.mycom.app
artifactId: hello-maven
version: 1.0-SNAPSHOT
package: com.mycom.app
 Y: : Y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /home/dreamer/workspace
[INFO] Parameter: package, Value: com.mycom.app
[INFO] Parameter: groupId, Value: com.mycom.app
[INFO] Parameter: artifactId, Value: hello-maven
[INFO] Parameter: packageName, Value: com.mycom.app
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /home/dreamer/workspace/hello-maven
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS//构建成功
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:09 min
[INFO] Finished at: 2018-03-10T16:46:33+08:00
[INFO] Final Memory: 18M/203M
[INFO] ------------------------------------------------------------------------

【疑】在之前我们自己建的时候,jar包的名字有一个流程的,还要加上version版本,为何在这里利用archetype建立的默认不是含有version的呢?

现在我们就有hello-maven的项目了

《【Java学习】从一个简单的HelloWorld项目中入门maven》

看一下骨架。。。

├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── com
    │           └── mycom
    │               └── app
    │                   └── App.java
    └── test
        └── java
            └── com
                └── mycom
                    └── app
                        └── AppTest.java

是不是省了很多工作呢?

到此,简单的HelloWorld项目就完成了,呼累死,也学到了很多。

参考文献

maven官方文档
maven实战,许晓斌,2017.3
各类文章,谢谢各位前辈

    原文作者:ztyzz
    原文地址: https://segmentfault.com/a/1190000013608321
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞