转 https://www.cnblogs.com/youzhibing/p/5427130.html
前言
项目中用到了maven,而且用到的内容不像利用maven/eclipse搭建ssm(spring+spring mvc+mybatis)用的那么简单;maven的核心是pom.xml,那么我就它来谈谈那些不同的地方;
给我印象最深的就是如下四个元素:modules、parent、properties、import。
路漫漫其修远兮,吾将上下而求索!
github:https://github.com/youzhibing
码云(gitee):https://gitee.com/youzhibing
modules
从字面意思来说,module就是模块,而pom.xml中的modules也正是这个意思,用来管理同个项目中的各个模块;如果maven用的比较简单,或者说项目的模块在pom.xml没进行划分,那么此元素是用不到的;不过一般大一点的项目是要用到的。
需求场景
如果我们的项目分成了好几个模块,那么我们构建的时候是不是有几个模块就需要构建几次了(到每个模块的目录下执行mvn命令)?当然,你逐个构建没问题,但是非要这么麻烦的一个一个的构建吗,那么简单的做法就是使用聚合,一次构建全部模块。
具体实现
a.既然使用聚合,那么就需要一个聚合的载体,先创建一个普通的maven项目account-aggregator,如下图:
因为是个聚合体,仅仅负责聚合其他模块,那么就只需要上述目录,该删除的就删了;注意的是pom文件的书写(红色标明的):
<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.youzhibing.account</groupId> <artifactId>account-aggregator</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Account Aggrregator</name> <url>http://maven.apache.org</url> <modules>
<!-- 模块都写在此处 --> <module>account-register</module> <module>account-persist</module> </modules> </project>
b.创建子模account-register、account-persist:右击account-aggregator,new --> other --> Maven,选择Maven Module,创建moven模块。
c.创建完成后,项目结构如下,那么此时account-aggregator可以收缩起来了,我们操作具体子模块就好了。
d.注意点,当我们打开包结构的子模块的pom文件时,发现离预期的多了一些内容,我们坐下处理就好了。
e.那么编码完了之后,我们只需要构建account-aggregator就好了,所有的子模块都会构建。
parent
继承,和java中的继承相当,作用就是复用
需求场景
若每个子模块都都用的了spring,那么我们是不是每个子模块都需要单独配置spring依赖了?,这么做是可以的,但是我们有更优的做法,那就是继承,用parent来实现。
具体实现
a.配置父pom.xml
我就用聚合pom来做父pom,配置子模块的公共依赖。
父(account-aggregator)pom.xml :
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
b.account-register的pom.xml :
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
c.account-persist的pom.xml :
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
d.依赖的jar包全部ok,需要做的则是在各个模块中进行代码开发了!
依赖管理
继承可以消除重复,那是不是就没有问题了? 答案是存在问题,假设将来需要添加一个新的子模块account-util,该模块只是提供一些简单的帮助工具,不需要依赖spring、junit,那么继承后就依赖上了,有没有什么办法了? 有,maven已经替我们想到了,那就是dependencyManagement元素,既能让子模块继承到父模块的依赖配置,又能保证子模块依赖使用的灵活性。在dependencyManagement元素下得依赖声明不会引入实际的依赖,不过它能够约束dependencies下的依赖使用。
在父pom.xml中配置dependencyManagement元素
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
<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.youzhibing.account</groupId> <artifactId>account-aggregator</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>pom</packaging> <name>Account Aggrregator</name> <url>http://maven.apache.org</url> <modules> <!-- 模块都写在此处 --> <module>account-register</module> <module>account-persist</module> </modules> <dependencyManagement> <dependencies> <!-- 配置共有依赖 --> <!-- spring 依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.0.2.RELEASE</version> </dependency> <!-- junit 依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.7</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> </project>
account-persist的pom.xml(account-register也一样) :
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
使用这种依赖管理机制似乎不能减少太多的POM配置,就少了version(junit还少了个scope),感觉没啥作用呀;其实作用还是挺大的,父POM使用dependencyManagement能够统一项目范围中依赖的版本,当依赖版本在父POM中声明后,子模块在使用依赖的时候就无须声明版本,也就不会发生多个子模块使用版本不一致的情况,帮助降低依赖冲突的几率。如果子模块不声明依赖的使用,即使该依赖在父POM中的dependencyManagement中声明了,也不会产生任何效果。
import
import只在dependencyManagement元素下才有效果,作用是将目标POM中的dependencyManagement配置导入并合并到当前POM的dependencyManagement元素中,如下就是讲account-aggregator中的dependencyManagement配置导入并合并到当前POM中。
<dependencyManagement> <dependencies> <dependency> <groupId>com.youzhibing.account</groupId> <artifactId>account-aggregator</artifactId> <version>1.0.0-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
properties
通过<properties>元素用户可以自定义一个或多个Maven属性,然后在POM的其他地方使用${属性名}的方式引用该属性,这种做法的最大意义在于消除重复和统一管理。
Maven总共有6类属性,内置属性、POM属性、自定义属性、Settings属性、java系统属性和环境变量属性;
内置属性
两个常用内置属性 ${basedir} 表示项目跟目录,即包含pom.xml文件的目录;${version} 表示项目版本
pom属性
用户可以使用该类属性引用POM文件中对应元素的值。如${project.artifactId}就对应了<project> <artifactId>元素的值,常用的POM属性包括:
${project.build.sourceDirectory}:项目的主源码目录,默认为src/main/java/
${project.build.testSourceDirectory}:项目的测试源码目录,默认为src/test/java/
${project.build.directory} : 项目构建输出目录,默认为target/
${project.outputDirectory} : 项目主代码编译输出目录,默认为target/classes/
${project.testOutputDirectory}:项目测试主代码输出目录,默认为target/testclasses/
${project.groupId}:项目的groupId
${project.artifactId}:项目的artifactId
${project.version}:项目的version,与${version} 等价
${project.build.finalName}:项目打包输出文件的名称,默认为${project.artifactId}-${project.version}
自定义属性
如下account-aggregator的pom.xml,那么继承了此pom.xml的子模块也可以用此自定义属性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
settings属性
与POM属性同理,用户使用以settings. 开头的属性引用settings.xml文件中的XML元素的值。
Java系统属性
所有java系统属性都可以用Maven属性引用,如${user.home}指向了用户目录。
环境变量属性
所有环境变量属性都可以使用以env. 开头的Maven属性引用,如${env.JAVA_HOME}指代了JAVA_HOME环境变量的的值。
聚合与继承的关系
1.聚合主要是为了方便快速构建项目,继承主要是为了消除重复配置;
2.对于聚合模块而言,它知道有哪些被聚合的模块,但那些被聚合的模块不知道这个聚合模块的存在;对于继承的父pom而言,它不知道有哪些子模块继承它,但那些子模块都必须知道自己的父POM是什么;
3.聚合POM与继承中的父POM的packaging都必须是pom;同时,聚合模块与继承中的父模块除了POM外,都没有实际的内容
结束语
maven越来越流行,这方面的资料也是越来越多,《Maven实战》给我的感觉就相当不错,本博客的内容大多取自其中;网上资料也越来越多,就博客园中就有不少;
maven发布版和快照版: <distributionManagement>
https://www.cnblogs.com/liu2-/p/9035181.html
在使用maven过程中,我们在开发阶段经常性的会有很多公共库处于不稳定状态,随时需要修改并发布,可能一天就要发布一次,遇到bug时,甚至一天要发布N次。我们知道,maven的依赖管理是基于版本管理的,对于发布状态的artifact,如果版本号相同,即使我们内部的镜像服务器上的组件比本地新,maven也不会主动下载的。如果我们在开发阶段都是基于正式发布版本来做依赖管理,那么遇到这个问题,就需要升级组件的版本号,可这样就明显不符合要求和实际情况了。但是,如果是基于快照版本,那么问题就自热而然的解决了,而maven已经为我们准备好了这一切。
maven中的仓库分为两种,snapshot快照仓库和release发布仓库。snapshot快照仓库用于保存开发过程中的不稳定版本,release正式仓库则是用来保存稳定的发行版本。定义一个组件/模块为快照版本,只需要在pom文件中在该模块的版本号后加上-SNAPSHOT即可(注意这里必须是大写),如下:
<groupId>cc.mzone</groupId> <artifactId>m1</artifactId> <version>0.1-SNAPSHOT</version> <packaging>jar</packaging>
maven会根据模块的版本号(pom文件中的version)中是否带有-SNAPSHOT来判断是快照版本还是正式版本。如果是快照版本,那么在mvn deploy时会自动发布到快照版本库中,而使用快照版本的模块,在不更改版本号的情况下,直接编译打包时,maven会自动从镜像服务器上下载最新的快照版本。如果是正式发布版本,那么在mvn deploy时会自动发布到正式版本库中,而使用正式版本的模块,在不更改版本号的情况下,编译打包时如果本地已经存在该版本的模块则不会主动去镜像服务器上下载。
所以,我们在开发阶段,可以将公用库的版本设置为快照版本,而被依赖组件则引用快照版本进行开发,在公用库的快照版本更新后,我们也不需要修改pom文件提示版本号来下载新的版本,直接mvn执行相关编译、打包命令即可重新下载最新的快照库了,从而也方便了我们进行开发。
接下来要介绍的是如何在项目中应用snapshot和release库,应用snapshot和release库达到不同环境下发布不同的版本的目的,首先看一个pom文件的定义:
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.aty.mybatis</groupId> <artifactId>mybatis-demo</artifactId> <packaging>jar</packaging> <version>${project.release.version}</version> <name>mybatis-demo</name> <url>http://maven.apache.org</url> <properties> <project.release.version>0.1-SNAPSHOT</project.release.version> </properties> <profiles> <profile> <id>release</id> <properties> <project.release.version>0.1</project.release.version> </properties> </profile> </profiles> <!--定义snapshots库和releases库的nexus地址--> <distributionManagement> <repository> <id>nexus-releases</id> <url> http://172.17.103.59:8081/nexus/content/repositories/releases/ </url> </repository> <snapshotRepository> <id>nexus-snapshots</id> <url> http://172.17.103.59:8081/nexus/content/repositories/snapshots/ </url> </snapshotRepository> </distributionManagement> </project>
首先我们看到pom文件中version的定义是采用占位符的形式,这样的好处是可以根据不同的profile来替换版本信息,比如maven默认是使用0.1-SNAPSHOT作为该模块的版本。
1、如果在发布时使用mvn deploy -P release 的命令,那么会自动使用0.1作为发布版本,那么根据maven处理snapshot和release的规则,由于版本号后不带-SNAPSHOT故当成是正式发布版本,会被发布到release仓库;
2、如果发布时使用mvn deploy命令,那么就会使用默认的版本号0.1-SNAPSHOT,此时maven会认为是快照版本,会自动发布到快照版本库。
在distributionManagement段中配置的是snapshot快照库和release发布库的地址,我这里是采用nexus作为镜像服务器。对于版本库主要是id和url的配置,配置完成后就可以通过mvn deploy进行发布了,当然了,如果你的镜像服务器需要用户名和密码,那么还需要在maven的settings.xml文件中做如下配置:
<server> <id>nexus-releases</id> <username>admin</username> <password>admin123</password> </server> <server> <id>nexus-snapshots</id> <username>admin</username> <password>admin123</password> </server>
注意这里配置的server的id必须和pom文件中的distributionManagement对应仓库的id保持一致,maven在处理发布时会根据id查找用户名称和密码进行登录和文件的上传发布。
我们这里通过profile的定义就可以在发布灵活切换snapshot快照版本和release正式版本了,在被依赖的组件中也可以使用profile来定义在开发阶段使用快照库,在发布阶段使用正式库的功能,只需要在不同的profile中覆盖默认的properties属性值即可。
idea中mvn deploy
-DskipTests,不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下。
-Dmaven.test.skip=true,不执行测试用例也不编译测试用例类。
maven问题:
1.] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project test-common-component-manage: Compilation failure
-source 1.6 中不支持 diamond 运算符 [ERROR] (请使用 -source 7 或更高版本以启用 diamond 运算符)
是由于你项目所需jdk版本和你当前使用的jdk版本不一致导致的,
解决办法:更换当前jdk版本为项目所需jdk版本即可
project-structure - project - 设置SDK,project language level
或者project-structure - modules - language level
或者setting,查看java compiler -- module - target bytecode version,可直接在父工程中设置,即可自动变化
<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>
1.编译工程时报错显示找不到符号,类位置信息,但是代码没有报错。。。
可按照网上教程设置相关变量均为utf - 8 或者clean,update project,主要因为该工程是某父工程下子工程,同时依赖了父工程下其他的子工程,当子工程编译有问题或者其他原因时,导致依赖的子工程出现问题
在父pom下,compile整个工程,会按modules逐个编译子工程,显示子工程编译success,再次编译该子工程,success,idea运行debug模式正常。
debug时,选择子工程war包,会先编译该工程,在target目录下生成编译好的war包,class文件,然后自动部署至tomcat webapps下,运行,若mvn clean,将情空target下war包
2.source 1.7 中不支持 lambda 表达式
pom文件中<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
3.maven项目引入本地的jar包,项目部署运行的时候,找不到该包时
https://www.cnblogs.com/liukunjava/p/9577768.html
直接在maven的pom里给springboot的打包插件引入一下参数就行
<includeSystemScope>true</includeSystemScope>
总体是这样的
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
4.springboot项目用maven打包,为什么在生成jar包的同时还生产.original文件?.jar.original
是普通jar包,不包含依赖.jar
是可执行jar包,包含了pom中的所有依赖,可以直接用java -jar
命令执行
如果是部署,就用.jar
如果是给别的项目用,就要给.jar.original
这个包
5.Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.0.2.RELEASE:repackage (default) on project www-service: Execution default of
goal org.springframework.boot:spring-boot-maven-plugin:2.0.2.RELEASE:repackage failed: Unable to rename 'D:\workspace\eclipse_data\vi\target\www-service-0.0.1-SNAPSHOT.jar' to
'D:\workspace\eclipse_data\vi\target\www-service-0.0.1-SNAPSHOT.jar.original' -> [Help 1]
可通过添加如下配置解决
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>boot</classifier>
</configuration>
</plugin>
6.springboot项目启动时报错:Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in class path resource,(项目没有redis启动相关,报错原因不详)
经查找,在Java代码中,我们使用了注解:@EnableRedisHttpSession,这个注解是用来开启Redis来集式式管理Session。
而在使用这种方式的时候,是需要Redis开启Keyspace Notifications功能的,默认是关闭的。
这个功能有一个参数来控制它,notify-keyspace-events,值为Egx。
可以通过在Redis.Config中配置。
也可以通过命令行来配置,如下所示:
redis-cli config set notify-keyspace-events Egx
然后重启Redis生效。
7.出现问题:线上部署运行正常,本地调试过后,启动时突然报错
Description:
Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
解决办法:
1)网上搜了很多,多数是说没有配置数据源,还有的说spring没有扫描到yml文件
2)但是,这个项目已正常开发2个月,同一套代码刚上线的没有问题,只是在本地启动报错。网上的情况都不适用。
3)最后解决办法:点击maven project 中的Lifecycle→clean,然后重启项目,成功。
4)update project后重启成功。
5)同一个项目,在eclipse中能正常启动,在idea中就报改错,分析原因是因为在idea中读不了application.yml配置文件,同样的目录结构在eclipse中能正常读取,在pom的<build></build>中加入中
<resources> <!--<resource> <directory>src/main/java</directory> <includes> <include>**/*.yml</include> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource>--> <resource> <directory>resources</directory> <includes> <include>*.yml</include> <include>*.xml</include> <include>*.properties</include> </includes> <filtering>false</filtering> </resource> </resources> 注意,这个resources文件与src文件同级,不同级时写法不一样
然后重新在idea中启动正常,且修改application.yml文件或者xml文件内容能正常获取新内容。
注意下:
idea在build工程的时候,遇到maven项目使用的是pom文件里面配置的<build></build>里面的东西 而这里面如果不做特别配置,是maven默认的,编译的时候,只搬运src/main/java里面的java文件到target/classes,其他文件会被忽略,所以找不到.xml文件。要在pom文件里面加入以下代码: <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>*/.properties</include> <include>*/.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>