Article Directory
Getting Started
project skeleton
For a project managed by Maven, Maven advocates the use of a directory structure standard:
${basedir} 存放pom.xml和所有的子目录
${basedir}/src/main/java 项目的java源代码
${basedir}/src/main/resources 项目的资源,比如说property文件,springmvc.xml
${basedir}/src/test/java 项目的测试类,比如说Junit代码
${basedir}/src/test/resources 测试用的资源
${basedir}/src/main/webapp/WEB-INF web应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面
${basedir}/target 打包输出目录
${basedir}/target/classes 编译输出目录
${basedir}/target/test-classes 测试编译输出目录
Test.java Maven只会自动运行符合该命名规则的测试类
~/.m2/repository Maven默认的本地仓库目录位置
Speaking of this, I can mention Archetype . Maven uses Archetype to generate project skeletons. In fact, these things above will automatically generate a blank project directory. Even pom.xml is generated for you, which is very considerate.
Of course, if you are using an IDE, the project skeleton will be automatically built for you, but if you must experience generating a project skeleton through the command line, it is also possible.
If it is Maven3, execute in the specified project parent directory:
mvn archetype:generate
This process is actually the process of running the plugin maven-archetype-plugin.
Then it will output many, many, many things. This is to show you the project skeletons to choose from, that is, Archetype. Each Archetype has a number. I looked at it and the number is 2423. . .
We can choose maven-archetype-quickstart
, enter and press Enter, and select by number.
Then maven will prompt you to enter the groupID, artifactId, version and package name package of the project to be created. Enter all, then OK Y:
The directory has been created.
Like src/main/java
, src/test/java
, pom.xml
this kind, it will be built for you.
The package name I set is org.wlh
, so it kindly generated one for me src/main/java/org/wlh/App.java
as the main file.
Archetype can help us quickly build the skeleton of the project, and it provides a variety of templates, which is great.
Just understand this, after all, the IDE has done it for us.
pom.xml
Like Makefile of Make and build.xml of Ant, the core of Maven project is pom.xml.
POM (Project Object Model, Project Object Model) defines the basic information of the project, such as how to build the project, declare project dependencies, and so on.
Here is an extremely raw 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.org/xsd/maven-4.0.0.xsd">
<!-- 模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 公司或者组织的唯一标志,并且配置时生成的路径也是由此生成, 如com.companyname.project-group,maven会将该项目打成的jar包放本地路径:/com/companyname/project-group -->
<groupId>com.companyname.project-group</groupId>
<!-- 项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的 -->
<artifactId>project</artifactId>
<!-- 版本号 -->
<version>1.0</version>
<name>Hello World Project</name>
</project>
The first line is the XML header, specifying the version and encoding of the xml document.
The rest are project elements, and project is the root element of pom.xml.
modelVersion specifies the version of the current pom model. For Maven2 and Maven3, it can only be 4.0.0.
The most important in this code are the three lines groupId, artifactId and version. These three elements basically define the base coordinates of an item.
- groupId: defines which group the item belongs to;
- artifactId: defines the unique ID of the current project within the group;
- version: the version of the current project;
- name: Not necessary, defines a more user-friendly project name.
Normally, the main code of the project should be placed in the src/main/java directory, and Maven will automatically search this directory to find the project entry class.
Secondly, the package name of the project entry class should be consistent with the groupId and artifactId defined in the POM, that is, the package name, groupId.artifactId
which is clearer and convenient for Maven's automatic search. Of course, it is possible to be different, and it has no major impact on operation.
build process
After the entry class is written, it can be compiled using Maven.
Run the command in the root directory:mvn clean compile
clean will tell Maven to clean up the output directory target/;
compile tells Maven to compile the main code of the project;
After completion, Maven will compile the main code of the project to the target/classes directory.
For example, I now declare a main class Hello World:
package org.wlh.helloworld;
public class Hello {
public static void main(String[] args) {
System.out.println(new Hello().HelloMaven());
}
public String HelloMaven(){
return "Hello Maven";
}
}
write tests
The test code in Maven should be placed src/test/java
in the directory.
The unit testing framework JUnit commonly used in Java projects. This dependency needs to be referenced in the POM when used.
<?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>org.wlh</groupId>
<artifactId>helloworld</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
The dependencies element is added to the code, which can contain multiple dependency elements to declare the dependencies of the project, and a dependency is a dependency.
The scope element under the dependency element indicates the dependency scope of the dependency :
- test: only valid for test code, if used in main code, compilation exception will be reported;
- compile: The default dependency range, which means that both the main code and the test code can be used.
Write the test class HelloTest under src/test/java:
package org.wlh.helloworld;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class HelloTest {
@Test
public void testHello(){
Hello hello = new Hello();
String result = hello.HelloMaven();
assertEquals("Hello Maven", result);
}
}
The above are the three steps of a typical unit test:
- Prepare test classes and data
- Execute the behavior to be tested
- test result
In Junit3, it is agreed that the test method that needs to be executed starts with test, and Junit4 still recommends following this rule. In addition, in Junit4, the test methods that need to be executed should be marked with @Test.
After the test case is written, the test can be executed. If it is through the mvn command line, then you need to runmvn clean test
package and run
Packaging :mvn clean package
In the output log, you can see the form of jar:jar, which is actually to package the main code of the project with the jar target of the jar plug-in.
If you want other Maven projects to directly reference this jar package as a dependency, you need to install this jar package to the local warehouse:
install :mvn clean install
It should be noted here that the jar generated by default packaging cannot be run directly. If you run java -jar directly, you will be prompted that there is no main list attribute in the jar package .
This is because the entry class with the main method will not be added to the manifest, that is, open the META-INF/MANIFEST.MF file in the jar file, and the Main-Class line cannot be seen.
In addition, if your project depends on other jar packages, Maven's default package will not include them, but will only package its own class files.
So what if you want to generate an executable jar file?
Two solutions:
- Add the main list attribute to the jar package generated in the previous step, that is, add a line in MANIFEST.MF:
Main-Class: org.wlh.helloworld.Hello
, which is the full path of the entry class, and then run java -jar. - With the help of other plugins, such as the maven-shade-plugin plugin, an executable jar package can be generated in one step.
Commonly used packaging methods
In Maven, there are actually 3 ways to package:
- maven-jar-plugin: the default packaging plug-in, used to make ordinary jar packages, not executable;
- maven-shade-plugin: used to create executable jar packages, the so-called fat jar packages , also supports dependent project filtering, etc.;
- maven-assembly-plugin: supports custom packaging structure, and can also customize dependent projects, etc.
The package created by maven-jar-plugin only contains the class file of the project itself, and does not include all the jar packages that the project depends on. In addition, the main-class is not specified by default, so the package produced in this way cannot be executed.
The packages produced by maven-shade-plugin and maven-assembly-plugin contain all jar packages that the project depends on, so they are all executable. The specific difference between the two lies in a bug of the maven-assembly-plugin .
A project generally depends on many jar packages, and these dependent jar packages may depend on other jar packages. In this case, the project itself may depend on different versions of the same package (assuming it is a spring package).
Under this premise, when using assembly packaging, only a certain version of the spring.schemas file can be put into the final jar package, which will cause problems.
When using shade for packaging, it can merge the spring.schemas files in all spring jar packages, so the final generated jar package is equivalent to containing all the spring versions that have appeared in the project.
Based on this problem, if you just want to create an executable package, it is recommended to use maven-shade-plugin .
2022-5-17 16:22:46 But it seems to need to be configured? See reference 3 for this.
Let's talk about how to generate executable files through the maven-shade-plugin plugin:
maven-shade-plugin
use
Add the following code to the pom.xml file:
Remember to replace the path in the mainClass node with the entry class of your project.
<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.wlh.helloworld.Hello</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
After the shade plugin is packaged, two jar packages will be generated in the target directory, one is origin-xxx.jar and the other is xxx.jar. Among them, origin-xxx.jar only contains the class file of the project itself, and xxx.jar contains the class of the project itself + the jar package on which it depends. Just use the second one.
And you can also see that the Main-Class line is included in xxx.jar.
filter and artifactSet - filter jar package
Use filter to exclude part of the contents of the jar package when packaging.
<include>/<exclude>
Marked by groupId:artifactId, more fine-grained control can be used inside the filter , both code files and configuration files can be removed.
<!-- 按package过滤junit包 -->
<configuration>
<filters>
<filter>
<artifact>junit:junit</artifact>
<includes>
<include>junit/framework/**</include>
<include>org/junit/**</include>
</includes>
<excludes>
<exclude>org/junit/experimental/**</exclude>
<exclude>org/junit/runners/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
If you want to filter out the entire jar package, you can use it <artifactSet>
, which is also the identification of specifying groupId:artifactId.
<configuration>
<artifactSet>
<excludes>
<exclude>classworlds:classworlds</exclude>
<exclude>junit:junit</exclude>
<exclude>jmock:*</exclude>
<exclude>*:xml-apis</exclude>
<exclude>org.apache.maven:lib:tests</exclude>
<exclude>log4j:log4j:jar:</exclude>
</excludes>
</artifactSet>
</configuration>
In addition, configure <minimizeJar>
to automatically remove dependencies that are not used in the project .
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
Transformer
The shade plugin provides a wealth of Transformer tool classes, here are some commonly used Transformers. For more Transformers, see http://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html
ManifestResourceTransformer
The function is to write the Main-Class to the MANIFEST file, which is a necessary condition for the executable package.
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.lcifn.Application</mainClass>
</transformer>
</transformers>
</configuration>
AppendingTransformer
It is used to handle the merging of the same configuration files in multiple jar packages, especially spring.
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</transformers>
</configuration>
ServicesResourceTransformer
JDK's service discovery mechanism is based on the META-INF/services/ directory. If there are multiple implementations of the same interface that need to be merged, you can use this Transformer.
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
references
- Quickly generating an executable jar package (super simple) through IDEA was unsuccessful, but the process is a bit interesting.
- The use of maven-shade-plugin, one of Maven's three packaging methods
- [ 1119 Use the maven plug-in maven-shade-plugin to package the executable java project and all its dependent jars