Maven (2): Getting Started with Maven

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默认的本地仓库目录位置

insert image description here

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:

insert image description here

The directory has been created.

Like src/main/java, src/test/java, pom.xmlthis 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.javaas 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.artifactIdwhich 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/javain 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:

  1. 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.
  2. 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

  1. Quickly generating an executable jar package (super simple) through IDEA was unsuccessful, but the process is a bit interesting.
  2. The use of maven-shade-plugin, one of Maven's three packaging methods
  3. [ 1119 Use the maven plug-in maven-shade-plugin to package the executable java project and all its dependent jars

Guess you like

Origin blog.csdn.net/wlh2220133699/article/details/131291494