【曹工雜談】Maven底層容器Plexus Container的前世今生,一代芳華終落幕


Maven底層容器Plexus Container的前世今生,一代芳華終落幕

前言

說實話,我非常地糾結,大家平時只是用Maven,對於內部的實現其實也不關心,我現在非要拉着大家給大家講。這就有個問題,Maven的內部,還是相對沒那么簡單的,也算是個不小的工程了。

核心功能,大家是清楚的,內部的執行流程,大家也大概猜的出來:

  1. 解析命令行參數
  2. 准備各種上下文,簡單的mvn clean就涉及到當前項目的元數據pom.xml、settings.xml(主要是本地、遠程倉庫相關);
  3. 根據mvn clean或者mvn compile,找到對應的生命周期(大家應該都知道maven的三個lifecycle吧);然后看看要執行生命周期中的哪些階段,順序是啥(這個和打包方式也有關,jar/war時,打包組件就不同);
  4. 順序執行生命周期中的每個階段的時候,去找到對應的綁定的插件,然后執行插件(執行插件又包括:根據插件坐標,去本地倉庫/遠程倉庫找對應的artifact,以及解析artifact中的插件元數據,元數據中會告訴你,支持傳哪些參數,參數類型是啥)
  5. 執行完成后,返回結果。

這么一個不小的工程,想必,還是會有很多對象互相依賴的,在沒有依賴注入前,都是靠new,或者是工廠來緩解;我查了下歷史資料,maven的開發者在一篇文章中(https://blog.sonatype.com/2010/01/from-plexus-to-guice-1-why-guice/),提到:

We knew we needed some sort of component framework, some standard mechanism to instantiate plugins and configure them based on a set of configuration points

大概意思,他們也發現他們需要一個組件框架,一種標准化能夠實例化組件的機制,能夠基於一組配置來配置這些組件。

說明,早期的Maven開發者們,已經意識到了自己可能需要一個類似容器的東西,那,為啥沒有選用Spring呢?

下邊,讓我們來層層揭開歷史的面紗。

Maven初起步

查閱了Maven官網,發現Maven的第一個版本,竟然早在2002年。(http://jakarta.apache.org/site/news/news-2002.html)。

2003年,成為Apache頂級項目,2004年,發布1.0版本。

另外一邊,我們熟知的,無人不知無人不曉的Spring呢,第一個版本是什么時候呢?

The first version was written by Rod Johnson, who released the framework with the publication of his book Expert One-on-One J2EE Design and Development in October 2002.

這里說,spring的第一個版本同樣發布於2002年,是跟隨着Rod Johnson的書《Expert One-on-One J2EE Design and Development 》一起發布的。也是在03年,使用Apache 2.0 License,發布了一個版本;04年3月,發布了一個生產用的版本。

所以,我們就可以理解,Maven的開發者在那篇2010年的文章里寫的:

When we started the Maven project, dependency injection was still developing. Spring was just starting out and the Avalon project at Apache was really the only IoC framework around

簡單說,就是,當開始搞Maven的時候,依賴注入還在發展當中。Spring剛起步,Avalon項目,也僅僅只是一個ioc框架。

既然外部不成熟,他們的重心也不在這些依賴注入框架上面,所以他們就基於自己的需求,自己搞了一個適合Maven的,它就叫:Plexus。

Maven早期:做自己的IOC容器

Plexus項目

Plexus: 發音(ˈpleksəs),a network of nerves or vessels in the body.

Plexus,中文意思,可能是神經網絡或者血管網絡,就因為血管是網狀的,像他么互聯網一樣,所以被拿來當一個框架名了嗎,maybe。

這個項目(官網:https://codehaus-plexus.github.io),定位是做容器。官網不知道為啥,這會打不開,但是我發現一個記錄互聯網歷史的網站:https://web.archive.org/web/20150323083530/http://plexus.codehaus.org/index.html。

Plexus項目,基於其中的Plexus Container子項目,應用程序可以使用基於組件的編程方式,構建模塊化的、可復用的組件。Plexus類似其他的IOC框架,如Spring,但它還額外提供了很多特性,如:組件生命周期管理、組件實例化策略、嵌套容器、組件配置、自動注入、組件依賴、各種依賴注入方式(如構造器注入、setter注入、字段注入)。

總的來說,我個人感覺,這些特性,Spring好像也有啊,哈哈。

Plexus 下組件

Plexus這么一個項目,當然不止容器,大概有如下幾個:

  • Plexus Classworlds,類加載器框架,Maven至今還在用,個人感覺也挺不錯,推薦學習學習;

  • Plexus Container,IOC容器,Maven 1.x/2.x在用,3.0版本后,Maven自身也沒有再使用了

  • Plexus Components

    Maven的工作就是和各種文件、目錄打交道,這期間,會沉淀出來很多公用組件:

    1. IO相關的,Plexus IO Components,它的maven坐標:
    <!-- https://mvnrepository.com/artifact/org.codehaus.plexus/plexus-io -->
    <dependency>
        <groupId>org.codehaus.plexus</groupId>
        <artifactId>plexus-io</artifactId>
        <version>3.2.0</version>
    </dependency>
    
    
    1. 歸檔相關的,Plexus Archiver Component,maven坐標:

      <!-- https://mvnrepository.com/artifact/org.codehaus.plexus/plexus-archiver -->
      <dependency>
          <groupId>org.codehaus.plexus</groupId>
          <artifactId>plexus-archiver</artifactId>
          <version>4.2.5</version>
      </dependency>
      
      
    2. cli相關,Plexus CLI

    3. 編譯相關,Plexus Compiler

    4. Digest/Hashcode相關,Plexus Digest / Hashcode Components

    5. 國際化相關,i18n

    還有些其他的,我懶得列舉了,大家自己看吧,https://web.archive.org/web/20150225072024/http://plexus.codehaus.org/plexus-components/

  • Plexus Maven Plugin,用來支持Maven插件

  • Plexus Utils,工具類,至今仍在用

Plexus組件的現狀

打開我本機的maven安裝目錄的lib,發現plexus相關的,僅剩少數幾個了,如,下圖的幾個工具:

還有下圖的啟動類:

當初說好的IOC容器,結果Maven怎么自己也不用了呢?我們來看看這個容器相關的組件吧。

容器相關的,一共4個maven組件。

這里面,plexus-component-annotations我們剛看到,還在用,他是干嘛的呢,就是類似於Spring里面的注解,比如@Service/@Controller這種。

而plexus-component-metadata,是一個maven插件,用來生成組件的xml,有點像我們的spring的xml時代,這個工程呢,就可以分析我們代碼,幫我們生成spring的bean.xml這種,就不需要手動配置依賴了。

這里面,真正的IOC容器實現,就是:plexus-container-default。

該組件,可以說是Maven的結發妻子,陪伴了Maven的青年時期,我們看看這個組件是什么時候第一次登場的。

下邊是它1.0版本的坐標:

<!-- https://mvnrepository.com/artifact/plexus/plexus-container-default -->
<dependency>
    <groupId>plexus</groupId>
    <artifactId>plexus-container-default</artifactId>
    <version>1.0-alpha-1</version>
</dependency>

時間是2005年。

而maven什么時候開始使用該容器呢,我沒有查到maven 1.x版本的pom依賴,但是在2.0.alpha版本,已經看到了該容器的身影:

此時,是2006年。

僅僅幾年后,在maven 3.0的版本中,已經不再有plexus ioc容器的身影,卻來了一個不速之客。

在開始說不速之客之前,我們還是要問問,plexus ioc容器,為啥就不行了呢?對這個歷史感興趣的,可以直接看:

https://blog.sonatype.com/2010/01/from-plexus-to-guice-1-why-guice/

為什么呢?因為時代變了,此時,Spring已經開始成為事實上的IOC容器標准,不過,雖然Spring在應用開發領域,所向披靡,但是在各種框架中,框架開發者們還是覺得Spring太重了,一下就要引入好幾個jar包,實在是過於臃腫。因此,google 在2007年的時候,就推出了一個輕量級的依賴注入框架,叫google guice。

此時,經過多年的迭代,在2010年前后,guice已經比較成熟了,在google內部也而得到了廣泛應用,且依賴注入這個領域,也在持續不斷地發展中,比如java官方定義了相關的標准api。

而此時,Maven的開發者們已經難以同時維護Plexus IOC容器(比如適配java官方新出標准,和周邊Spring兼容等等各類工作),因此,Maven決定,為了節省精力,Maven將不再基於Plexus IOC容器,而是使用Guice,以后就只管用了,而guice的維護升級,自然有Guice的開源團隊去跟進。

說了那么多,為了紀念Plexus Container的落幕,我們還是來看看,這個IOC組件到底怎么用的吧?

Plexus IOC容器初使用

例子也是來自於官網,我根據文檔,整理成了一個maven module。大家可以拉代碼下來。

https://gitee.com/ckl111/maven-3.8.1-source-learn/tree/master/my-test-modules/plexus-ioc-container-test

1.像所有的IOC容器一樣,定義一個接口和實現類

public interface Cheese
{
    /** The Plexus role identifier. */
    String ROLE = Cheese.class.getName();

    /**
     * Slices the cheese for apportioning onto crackers.
     * @param slices the number of slices
     */
    void slice( int slices );

    /**
     * Get the description of the aroma of the cheese.
     * @return the aroma
     */
    String getAroma();
}
public class ParmesanCheese
        implements Cheese
{
    public void slice( int slices )
    {
        throw new UnsupportedOperationException( "No can do" );
    }

    public String getAroma()
    {
        return "strong";
    }
}

就像spring的xml時代一樣,定義組件的依賴關系

注意一下,這里的組件配置中,有三個元素:

  • role,此處放:接口名稱
  • role-hint,此處放:實現類的qualifier,類似spring中,一個接口多個實現類,我們就會給這多個實現類,定義一個顯示的名字,@Qualifier
  • implementation,實現類的類名。

ok,這就定義好了一個組件。

我們開始測試。

從容器中獲取組件

public class App {
    public static void main(String args[]) throws Exception
    {
        // 1  定義一個容器,容器會去加載classpath下的META-INF/Plexus/component.xml中的組件
        PlexusContainer container= new DefaultPlexusContainer();
        // 2 獲取組件,完成依賴注入等工作
        Cheese cheese = (Cheese) container.lookup( Cheese.ROLE, "parmesan" );
        // 3 使用組件
        System.out.println( "Parmesan is " + cheese.getAroma() );
        // 4 銷毀容器
        container.dispose();
    }
}

其他用法

如果這個組件,依賴其他組件,怎么辦呢,怎么注入呢?

我在maven源碼工程里看到這樣的組件配置,想必,就是像如下這樣配置。

<?xml version="1.0"?>
<component-set>
  <components>
    <component>
      <role>org.apache.maven.profiles.activation.ProfileActivator</role>
      <role-hint>faulty</role-hint>
      <implementation>org.ext.App</implementation>
      <requirements>
        <requirement>
          <role>org.apache.maven.artifact.ArtifactResolver</role>
          <field-name>artifactResolver</field-name>
        </requirement>
      </requirements>
    </component>
  </components>
</component-set>

循環依賴怎么辦呢?放心,人家也是可以解決的,這里就不截圖了。

總結

一個組件,寫出來,竟然感覺就像是也有感情一樣,也是有點意思。不過不管怎么說,Plexus Container在陪伴Maven度過了整個2.x版本后,終將落下帷幕。

接下來,是Guice的時代,而現在,十多年后的2021年,Guice依然穩定地支撐着Maven。Guice足夠優秀,在此之前,我竟然幾乎沒什么了解,Guice在哪些地方有應用呢,簡單列舉幾個:

  • google內部
  • scalatest
  • TestNG
  • Caffeine Cache
  • Spring Security Config
  • elastic search
  • jenkins

以及一些其他的我不太熟悉的技術,大家可以查看:

https://mvnrepository.com/artifact/com.google.inject/guice/usages


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM