2012-03-15 107 views
2

我熟悉Maven插件的開發,我需要一個問題的幫助。將類傳遞給Maven2插件

據我所知,將一個複雜的對象傳遞給一個maven插件是非常容易的,但通過一個類怎麼樣? 可以說我即將編寫自己的插件,其中只包含2個類。

Message.java

package abstractmessage; 
public abstract class Message { 
    public abstract String getMessage(); 
} 

MessageMojo.java

/** 
* @goal message 
*/ 
public class MessageMojo extends AbstractMojo { 
    /** 
    * @parameter expression="${message.messageClass}" 
    */ 
    private String messageClass ; 

    @Override 
    public void execute() throws MojoExecutionException, MojoFailureException { 
     Message message = null; 

     try { 
      Class<?> clazz = Class.forName(messageClass); 
      message = (Message) clazz.newInstance(); 
     } catch (Exception e) { 
      getLog().error(e); 
     } 

     getLog().info(message.getMessage()); 
    } 
} 

所以插件期望的擴展消息類的名稱,並試圖dynamicaly初始化。

真正的Maven項目中擴展的Message類看起來像這樣。

package message; 
import abstractmessage.Message; 

public class RealMessage extends Message { 
    @Override 
    public String getMessage() { 
     return "Hello plugin. From MavenProject."; 
    } 
} 

而pom.xml使用插件。

<build> 
    <plugins> 
     <plugin> 
      <artifactId>message-plugin</artifactId> 
      <groupId>message-plugin</groupId> 
      <version>1.0</version> 
      <executions> 
       <execution> 
        <phase>test</phase> 
        <goals> 
         <goal>message</goal> 
        </goals> 
       </execution> 
      </executions> 
      <configuration> 
       <messageClass>message.RealMessage</messageClass> 
      </configuration> 
     </plugin> 
    </plugins> 
</build> 

<dependencies> 
    <dependency> 
     <artifactId>message-plugin</artifactId> 
     <groupId>message-plugin</groupId> 
     <version>1.0</version> 
    </dependency> 
</dependencies> 

但是,當我執行行家:測試我得到一個java.lang.ClassNotFoundException: message.RealMessage

爲什麼會這樣?

+0

如果沒有明確使用該類,那麼它必須是一個字符串,您將重新解釋爲一個類名,唯一的方法就是根據我的知識製作自己的插件。 – Neil 2012-03-15 16:59:04

回答

1

您的插件的類加載器無權訪問項目的類。它只能訪問自己的類和它的依賴關係。

您有兩種選擇。

選項一是創建一個新的類加載器,用於加載當前項目及其依賴關係中的類。 surefire plugin是一個插件的例子 - 它可以加載已編譯的單元測試類並在它不處於分叉模式時運行它們。

從依賴關係創建一個新的類加載器並非完全無關緊要,但首先您需要將@requiresDependencyResolution test放在您的Mojo上,並獲取項目的依賴關係(以及項目的構建輸出目錄在搜索中包含項目的類)並從這些JAR和目錄構建一個URLClassLoader。 Surefire的generateTestClasspath() method可能有助於作爲一個起點。

另一個選擇是在單獨的項目中編譯您的RealMessage類,構建一個單獨的JAR並將其作爲依賴項添加到消息插件中。在這種情況下

你構建部分看起來是這樣的:

<build> 
    <plugins> 
     <plugin> 
      <artifactId>message-plugin</artifactId> 
      <groupId>message-plugin</groupId> 
      <version>1.0</version> 
      <executions> 
       <execution> 
        <phase>test</phase> 
        <goals> 
         <goal>message</goal> 
        </goals> 
       </execution> 
      </executions> 
      <configuration> 
       <messageClass>message.RealMessage</messageClass> 
      </configuration> 
      <dependencies> 
       <groupId>message-plugin-additions</groupId> 
       <artifactId>message-plugin-additions</artifactId> 
       <version>1.0</version> 
      </dependencies> 
     </plugin> 
    </plugins> 
</build> 

其中message-plugin-additions項目包含message.RealMessage類。如果你願意使用一個單獨的項目,這可能會比第一個選項簡單得多。

+0

該死的即時通訊新增至stackoverflow ... igonre this comment – 2012-03-16 16:32:46

1

那麼我試着構建一個新的URLClassLoader並將其鏈接到當前的ClassLoader。

@Override 
public void execute() throws MojoExecutionException, MojoFailureException { 
    //a direct absolute path just for testning 
    chainClassLoader(new File("C:\\Users\\admin\\.m2\\repository\\message-plugin\\real-message\\1.0\\real-message-1.0.jar")); 
    Message message = null; 

    try { 
     Class<?> clazz = Class.forName(messageClass); 
     message = (Message) clazz.newInstance(); 
    } catch (Exception e) { 
     getLog().error(e); 
    } 

    getLog().info(message.getMessage()); 
} 

private void chainClassLoader(File file) throws Exception { 
     ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); 
     URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{file.toURI().toURL()}, currentClassLoader); 
     Thread.currentThread().setContextClassLoader(urlClassLoader); 
} 

但我仍然得到一個ClassNotFoundException。

另外我如何確切地「抓住項目的依賴」與@requiresDependencyResolution

編輯:

好吧,我想我明白了。插件中的ClassLoader層次結構如下所示。

null 
    sun.misc.Launcher$ExtClassLoader 
    sun.misc.Launcher$AppClassLoader 

null 
    org.codehaus.classworlds.RealmClassLoader 
    java.net.URLClassLoader // my chained classloader 

Class.forName(clazz)試圖通過與AppClassLoader這代表它開始他的父母ExtClassLoader找到clazz中。但是,因爲我添加了新的類路徑到URLClassLoader這看起來在另一個ClassLoader層次結構中,我得到一個ClassNotFoundException

解決的辦法是不使用Class.forName(clazz),但Thread.currentThread().getContextClassLoader().loadClass(clazz)而是因爲我在鏈接到URLClassLoader線程的ContextClassLoader