如何在 Maven 原型中提供自定义逻辑?

问题描述 投票:0回答:2

我对创建 Maven 原型很感兴趣,并且我认为我已经掌握了大部分基础知识。然而,我坚持的一件事是有时我想使用自定义逻辑来填充模板。例如,如果有人生成我的原型并将artifactId指定为hello-world,我想生成一个名为HelloWorld的类,它只打印出“Hello World!”到控制台。如果另一个人使用artifactId = howdy-there生成它,则生成的类将是HowdyThere,并且它将打印出“Howdy There!”。

我知道,在幕后,Maven 的原型机制利用了 Velocity 模板引擎,因此我阅读了这篇关于 创建自定义指令 的文章。这似乎就是我正在寻找的,所以我创建了一个名为 HyphenatedToCamelCaseDirective 的类,它扩展了 org.apache.velocity.runtime.directive.Directive。在该类中,我的 getName() 实现返回“hyphenatedCamelCase”。在我的 archetype-metadata.xml 文件中,我有以下内容...

<requiredProperties>
    <requiredProperty key="userdirective">
        <defaultValue>com.jlarge.HyphenatedToCamelCaseDirective</defaultValue>
    </requiredProperty>
</requiredProperties>

我的模板类看起来像这样...

package ${package};

public class #hyphenatedToCamelCase('$artifactId') {

    // userdirective = $userdirective
    public static void main(String[] args) {
        System.out.println("#hyphenatedToCamelCase('$artifactId')"));
    }
} 

安装原型然后通过指定 artifactId = howdy-there 和 groupId = f1.f2 执行原型:生成后,生成的类如下所示...

package f1.f2;

public class #hyphenatedToCamelCase('howdy-there') {

    // userdirective = com.jlarge.HyphenatedToCamelCaseDirective    
    public static void main(String[] args) {
        System.out.println("#hyphenatedToCamelCase('howdy-there')"));
    }
}

结果显示,尽管 userdirective 是按照我预期的方式设置的,但它并没有像我希望的那样评估 #hyphenatedToCamelCase 指令。在指令类中,我有 render 方法将消息记录到 System.out,但该消息不会显示在控制台中,因此这让我相信该方法从未在 archetype:generate 期间执行。

我在这里错过了一些简单的东西,还是这种方法不是正确的方法?

maven velocity archetypes
2个回答
5
投票

archetype-metatadata xml 的必需属性部分用于将附加属性传递到速度上下文,它意味着传递速度引擎配置。因此,设置一个名为 userDirective 的属性只会使变量 $userDirective 可用,而不会向速度引擎添加自定义指令。

如果您查看源代码,maven-archetype 插件使用的速度引擎不依赖于任何外部属性源进行配置。 生成项目的代码依赖于VelocityComponent的自动装配(通过 plexus 容器)实现。

这是初始化速度引擎的代码:

public void initialize()
    throws InitializationException
{
    engine = new VelocityEngine();

    // avoid "unable to find resource 'VM_global_library.vm' in any resource loader."
    engine.setProperty( "velocimacro.library", "" );

    engine.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, this );

    if ( properties != null )
    {
        for ( Enumeration e = properties.propertyNames(); e.hasMoreElements(); )
        {
            String key = e.nextElement().toString();

            String value = properties.getProperty( key );

            engine.setProperty( key, value );

            getLogger().debug( "Setting property: " + key + " => '" + value + "'." );
        }
    }

    try
    {
        engine.init();
    }
    catch ( Exception e )
    {
        throw new InitializationException( "Cannot start the velocity engine: ", e );
    }
}

有一种添加自定义指令的巧妙方法。您在上面看到的属性是从 plexus-velocity-1.1.8.jar 中的 components.xml 文件中读取的。因此,打开此文件并添加您的配置属性

<component-set>
  <components>
    <component>
      <role>org.codehaus.plexus.velocity.VelocityComponent</role>
      <role-hint>default</role-hint>
      <implementation>org.codehaus.plexus.velocity.DefaultVelocityComponent</implementation>
      <configuration>
        <properties>
          <property>
            <name>resource.loader</name>
            <value>classpath,site</value>
          </property>
          ...
          <property>
            <name>userdirective</name>
            <value>com.jlarge.HyphenatedToCamelCaseDirective</value>
          </property>
        </properties>
      </configuration>
    </component>
  </components>
</component-set>

接下来将自定义指令类文件添加到此 jar 并运行 archetype:generate。

如您所见,这非常脆弱,您需要找到一种方法来分发这个被黑的 plexus-velocity jar。根据您计划使用此原型的用途,可能值得付出努力。


0
投票

参加聚会已经很晚了,但只是为了添加 6ton 在 Components.xml 上的答案:只要它位于 /META-INF/plexus/components.xml 中,在 Components.xml 文件中拥有自己的组件就足够了,因此无需尝试使用/分发 plexus-velocity 的更改版本

参见 https://codehaus-plexus.github.io/guides/developer-guide/configuration/index.html,其中还提到了使用 plexus.xml 指向组件的可能性。如果您正在制作您或其他人可能希望在某个时候重用的东西,那么拥有自动发现的 Components.xml 似乎是一种可行的方法。只需将 jar 添加到构建中即可自动获取它,这比每次都配置 xml 更不容易出错。

© www.soinside.com 2019 - 2024. All rights reserved.