maven的插件
我們知道Maven具體構建動作都是由插件執行的,maven本身只是提供一個框架,這樣就提供了高度可定制化的功能,我們用maven命令執行比如mvn clean package這樣的命令時maven會將package這個階段(phase)綁定到相應的生命周期(lifecycle),再尋找項目(project)里配置的plugin,執行具體的plugin完成持續構建
maven綁定插件(plugin)
maven在讀取命令行之后會根據命令行參數是系統默認的phase還是其他的自定義插件(goal)來解析成task參數,繼而根據這些task參數來生成插件執行列表。
for ( Object task : tasks ) { if ( task instanceof GoalTask ) { String pluginGoal = ( (GoalTask) task ).pluginGoal;
MojoDescriptor mojoDescriptor = mojoDescriptorCreator.getMojoDescriptor( pluginGoal, session, project );
MojoExecution mojoExecution =
new MojoExecution( mojoDescriptor, "default-cli", MojoExecution.Source.CLI );
mojoExecutions.add( mojoExecution );
}
else if ( task instanceof LifecycleTask )
{
String lifecyclePhase = ( (LifecycleTask) task ).getLifecyclePhase();
Map<String, List<MojoExecution>> phaseToMojoMapping =
calculateLifecycleMappings( session, project, lifecyclePhase );
for ( List<MojoExecution> mojoExecutionsFromLifecycle : phaseToMojoMapping.values() )
{
mojoExecutions.addAll( mojoExecutionsFromLifecycle );
}
}
else
{
throw new IllegalStateException( "unexpected task " + task );
}
}
代碼里先判斷task的類型,如果是GoalTask就說明參數是clean:clean這一種帶goal的格式,這種情況可能是maven內置插件也可能是開發者自己開發的插件,這種情況下maven就會先根據插件的goal去找到具體的插件,找的方法是先從項目定義的插件里找,找不到的話再去倉庫里找,這里把核心部分代碼貼出來:
PluginDescriptor pluginDescriptor =
pluginManager.loadPlugin( plugin, request.getRepositories(), request.getRepositorySession() );
if ( request.getPrefix().equals( pluginDescriptor.getGoalPrefix() ) )
{
return new DefaultPluginPrefixResult( plugin );
}
這個這部分比較簡單,就是遍歷項目里的插件一個個加載,然后比較這個插件的goal前綴是否和命令行的請求相同,如果相同的話直接封裝下該插件返回。其他用groupId:artifactId:version:goal格式的命令行解析也是差不多的方法,根據這些信息加載響應的plugin插件。接下來要重點說的是比較復雜的情況,即clean package這種比較內置的phase,下面代碼一一解讀: 先根據lifecyclephase來決定是哪個lifecycle,比如package這個phase就是在default生命周期里,maven內置定義了clean、default、site三個生命周期,maven采用plexus作為IOC容器,這個defaultLifeCycles的依賴是在maven-core的component.xml中定義的,與此同時定義了各個生命周期里的phase,這個讀者感興趣可以去看相應的代碼,此處略去不表。回到這里的代碼,maven根據phase去默認找生命周期,這里通過package找到了default生命周期。
/* * Determine the lifecycle that corresponds to the given phase. */ Lifecycle lifecycle = defaultLifeCycles.get( lifecyclePhase ); if ( lifecycle == null ) { throw new LifecyclePhaseNotFoundException( "Unknown lifecycle phase \"" + lifecyclePhase + "\". You must specify a valid lifecycle phase" + " or a goal in the format <plugin-prefix>:<goal> or" + " <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: " + defaultLifeCycles.getLifecyclePhaseList() + ".", lifecyclePhase ); }
接下來遍歷default lifecycle下所有的phase直到package這個phase就退出遍歷循環,這里體現了maven的一個特性,就是如果你指定了某個生命周期中某個phase,那這個phase之前的phase都會被執行,這里主要是初始化mappings包含哪些phase,每個phase的插件列表之所以是TreeMap是因為后面要根據優先級,也就是Key來排序遍歷確定插件執行順序,每個phase具體要執行的插件到下一段代碼再寫入。
/*
* Initialize mapping from lifecycle phase to bound mojos. The key set of this map denotes the phases the caller
* is interested in, i.e. all phases up to and including the specified phase.
*/
Map<String, Map<Integer, List<MojoExecution>>> mappings =
new LinkedHashMap<String, Map<Integer, List<MojoExecution>>>();
for ( String phase : lifecycle.getPhases() )
{
Map<Integer, List<MojoExecution>> phaseBindings = new TreeMap<Integer, List<MojoExecution>>();
mappings.put( phase, phaseBindings );
if ( phase.equals( lifecyclePhase ) )
{
break;
}
}
接下來遍歷本項目中所有插件,每個插件在遍歷所有執行配置,如果execution配置里已經指定了phase,則將這個execution下所有goal對應的Mojo加到對應phase的執行map里,如果execution配置里沒有指定phase的話,那就要去遍歷這個execution下所有goal,依次獲取該goal的Mojo描述信息,根據每個Mojo綁定的phase來將該Mojo加到對應phase的執行map里。
/*
* Grab plugin executions that are bound to the selected lifecycle phases from project. The effective model of
* the project already contains the plugin executions induced by the project's packaging type. Remember, all
* phases of interest and only those are in the lifecyle mapping, if a phase has no value in the map, we are not
* interested in any of the executions bound to it.
*/
for ( Plugin plugin : project.getBuild().getPlugins() )
{
for ( PluginExecution execution : plugin.getExecutions() )
{
// if the phase is specified then I don't have to go fetch the plugin yet and pull it down
// to examine the phase it is associated to.
if ( execution.getPhase() != null )
{
Map<Integer, List<MojoExecution>> phaseBindings = mappings.get( execution.getPhase() );
if ( phaseBindings != null )
{
for ( String goal : execution.getGoals() )
{
MojoExecution mojoExecution = new MojoExecution( plugin, goal, execution.getId() );
mojoExecution.setLifecyclePhase( execution.getPhase() );
addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() );
}
}
}
// if not then i need to grab the mojo descriptor and look at the phase that is specified
else
{
for ( String goal : execution.getGoals() )
{
MojoDescriptor mojoDescriptor =
pluginManager.getMojoDescriptor( plugin, goal, project.getRemotePluginRepositories(),
session.getRepositorySession() );
Map<Integer, List<MojoExecution>> phaseBindings = mappings.get( mojoDescriptor.getPhase() );
if ( phaseBindings != null )
{
MojoExecution mojoExecution = new MojoExecution( mojoDescriptor, execution.getId() );
mojoExecution.setLifecyclePhase( mojoDescriptor.getPhase() );
addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() );
}
}
}
}
}
經過前面幾個步驟之后,已經拿到了所有phase對應的Mojo執行列表,接下來需要將所有phase的Mojo串起來到一個總的列表里,這里注意mappings是一個LinkedHashMap,所以遍歷的時候是有順序的,而每個phase的execution map是TreeMap,根據優先級排序,這樣最后總體的順序是先按照總體的phase順序,再按照phase內的優先級進行排序。
Map<String, List<MojoExecution>> lifecycleMappings = new LinkedHashMap<String, List<MojoExecution>>();
for ( Map.Entry<String, Map<Integer, List<MojoExecution>>> entry : mappings.entrySet() )
{
List<MojoExecution> mojoExecutions = new ArrayList<MojoExecution>();
for ( List<MojoExecution> executions : entry.getValue().values() )
{
mojoExecutions.addAll( executions );
}
lifecycleMappings.put( entry.getKey(), mojoExecutions );
}
