[深入Maven原始碼]maven綁定命令列參數到具體外掛程式,maven命令列參數

來源:互聯網
上載者:User

[深入Maven原始碼]maven綁定命令列參數到具體外掛程式,maven命令列參數
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 );        }

  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.