clean pom
基础知识
-
依赖调解 dependency mediation
- nearest A->B->C2; A->C1 最终会选择C1
- 声明优先 A->C2; B->C1, 最终会选择C2
-
依赖管理 dependency management
- 可以显式指定,来解决多版本问题(传递依赖)。
-
依赖作用域
- compile、test、provided、system、runtime、import
-
排除依赖 excluded dependencies
-
可选依赖 optional dependencies
- X-> A A->B 可以将B声明为optional,这样X就不会依赖B,如果X想引入B依赖,需显式声明,eg: 可选例子
有一个名为X2的项目,这个项目和hibernate有一些类似的功能,支持许多数据库驱动/依赖,比如说MySQL,postgre,oracle等。为了构建X2,所有的这些依赖都是必须的,但是对于你的项目来说却不是必须的,所以对于X2把这些依赖声明为可选的是非常实用的,不论什么时候当你在POM文件中把X2声明为一个直接依赖的时候,所有被X2支持的驱动不会自动的被包含进你的项目的classpath,你需要直接声明你将要使用的数据库的依赖/驱动。
- 优先级
- 依赖管理显式引入的优先级高于依赖调解机制调解出来的
- 当前pom的声明优先于父pom中的声明(对于dependency的,区分是groupId:artifactId:type:classifier,四个都相同是前提;对于plugin是groupId和artifactId),详见下代码
- dependency management takes precedence over dependency mediation for transitive dependencies
- the current pom’s declaration takes precedence over its parent’s declaration.
子pom的dependencies和父pom的dependencies如何merge
包括dependencyManagement中的dependencies和真正的dependencies,以源码做注解maven github地址
// target:这里是子pom的dependencyManagement
// source:这里是父pom的dependencyManagement
// sourceDominant:是否source占主导,这里是false
protected void mergeDependencyManagement_Dependencies( DependencyManagement target, DependencyManagement source,
boolean sourceDominant, Map<Object, Object> context )
{
List<Dependency> src = source.getDependencies();
if ( !src.isEmpty() )
{
List<Dependency> tgt = target.getDependencies();
Map<Object, Dependency> merged = new LinkedHashMap<>( ( src.size() + tgt.size() ) * 2 );
// 注意dependency是包含excludes的。
for ( Dependency element : tgt )
{
Object key = getDependencyKey( element );
merged.put( key, element );
}
for ( Dependency element : src )
{
// key是groupId+artifactId+type+classifier组成的字符串,见下getManagementKey
Object key = getDependencyKey( element );
// sourceDominant为false
if ( sourceDominant || !merged.containsKey( key ) )
{
merged.put( key, element );
}
}
target.setDependencies( new ArrayList<>( merged.values() ) );
}
}
@Override
protected Object getDependencyKey( Dependency dependency )
{
return dependency.getManagementKey();
}
public String getManagementKey()
{
if ( managementKey == null )
{
managementKey = groupId + ":" + artifactId + ":" + type + ( classifier != null ? ":" + classifier : "" );
}
return managementKey;
}
dependency如何使用dependencyManagement的配置
public void mergeManagedDependencies( Model model )
{
DependencyManagement dependencyManagement = model.getDependencyManagement();
if ( dependencyManagement != null )
{
Map<Object, Dependency> dependencies = new HashMap<>();
Map<Object, Object> context = Collections.emptyMap();
for ( Dependency dependency : model.getDependencies() )
{
// 同上 groupId:artifactId:type:classifier
Object key = getDependencyKey( dependency );
dependencies.put( key, dependency );
}
for ( Dependency managedDependency : dependencyManagement.getDependencies() )
{
Object key = getDependencyKey( managedDependency );
Dependency dependency = dependencies.get( key );
if ( dependency != null )
{
mergeDependency( dependency, managedDependency, false, context );
}
}
}
}
protected void mergeDependency( Dependency target, Dependency source, boolean sourceDominant,
Map<Object, Object> context )
{
mergeDependency_GroupId( target, source, sourceDominant, context );
mergeDependency_ArtifactId( target, source, sourceDominant, context );
mergeDependency_Version( target, source, sourceDominant, context );
mergeDependency_Type( target, source, sourceDominant, context );
mergeDependency_Classifier( target, source, sourceDominant, context );
mergeDependency_Scope( target, source, sourceDominant, context );
mergeDependency_SystemPath( target, source, sourceDominant, context );
mergeDependency_Optional( target, source, sourceDominant, context );
mergeDependency_Exclusions( target, source, sourceDominant, context );
}
protected void mergeDependency_Version( Dependency target, Dependency source, boolean sourceDominant,
Map<Object, Object> context )
{
String src = source.getVersion();
if ( src != null )
{
// 如果dependency中没有声明版本,会取dependencyManagement的(groupId、artifactId、type、classifier、scope、systemPath、optional类似)
if ( sourceDominant || target.getVersion() == null )
{
target.setVersion( src );
target.setLocation( "version", source.getLocation( "version" ) );
}
}
}
@Override
protected void mergeDependency_Exclusions( Dependency target, Dependency source, boolean sourceDominant,
Map<Object, Object> context )
{
List<Exclusion> tgt = target.getExclusions();
// 只有当tgt的exclusions为空时,才会取dependencyManagement中的exclusions
if ( tgt.isEmpty() )
{
List<Exclusion> src = source.getExclusions();
for ( Exclusion element : src )
{
Exclusion clone = element.clone();
target.addExclusion( clone );
}
}
}
最佳实践
-
项目的parent设置为公共父pom(指定了一些每个项目都需要的配置,不需要你再重复了,包括jdk配置、plugin配置、仓库配置、常用jar的dependencyManagement;引入冲突检测机制,将风险在编译期就暴露出来)
-
依赖冲突都在项目的父模块中解决,子模块中引入
-
尽量使用依赖(dependencyManagement)解决冲突,而不是依赖调解exclude
-
统一日志,使用slf4j + log4j2,详见日志相关
-
线上只引release包,禁止引snapshot包。当然一点点来吧。
-
jar禁止包含工程配置文件:默认屏蔽的文件有:.xml,.properties
调试maven
主要是想验证下上边一些不确定的地方,官网没找到相关内容,网上说的不可信,就自己debug源码看了。
// 1. 下载maven源码
git clone git@github.com:apache/maven.git
// 2. build maven(你机器上得有maven和jdk)
mvn -DdistributionTargetFolder="/path/to/maven" clean package
// 3. 使用你源码build出的命令(不是你之前安装的哟),mvnDebug会设置java的debug端口为8000
cd /path/to/you_project
/path/to/maven/bin/mvnDebug clean package(你要执行的命令)
// 4.设置idea的remote debug配置为localhost 8000,入口是MavenCli#main,
// 5.调试吧。
参考
- 上一篇 logback日常总结
- 下一篇 maven基本命令