Maven & JSR-330
为什么选择 JSR-330?
Maven 通过Plexus使用依赖注入 (DI) 的历史由来已久,因此使用 JSR-330的目的是用某种标准替换自定义 DI 机制。Maven 使用的实现——从 3.0-beta-3 开始——被命名为Sisu,它基于直接支持 JSR-330的Guice 3.x。
如果您当前正在使用Plexus 注释和 API,则无需急于切换,也无需大刀阔斧的转换:Plexus、JSR-330 和 Guice API 都在 Maven 的核心中愉快地共存,您可以选择在以下情况下使用 JSR-330你希望。有数百个组件是使用 Plexus API 编写的。
如果您想使用 JSR-330,您必须了解您的代码不会与 Maven 3.0.x 兼容,而只能与 Maven 3.1.0 及更高版本兼容。尽管 JSR-330 自 Maven 3.0-beta-3 以来已在核心中可用,但它仅在 Maven 3.1.0 中可用于插件和扩展(有关详细信息,请参阅MNG-5343 )。
如果您对从 Plexus 迁移到 Guice 和 JSR-330 的背景感兴趣,可以参考以下文章:
如果您有兴趣从 Plexus 迁移到 Sisu,Sisu 有一个为您完成的Plexus 迁移文档。
如何使用 JSR-330
当您在 Maven 插件或扩展中使用 JSR-330 时,您需要在构建中设置两件事:
-
首先你需要一个依赖,
javax.inject
这样你就可以在你的插件和扩展中使用@Inject
,@Named
, 和注解。@Singleton
-
其次,您需要设置
sisu-maven-plugin
索引以供 Maven 使用的 JSR-330 组件。在sisu-maven-plugin
中创建其索引META-INF/sisu/javax.inject.Named
。
如果您使用下一段中的示例查看该文件,您将看到如下内容:
org.apache.maven.lifecycle.profiler.LifecycleProfiler
org.apache.maven.lifecycle.profiler.internal.DefaultSessionProfileRenderer
org.apache.maven.lifecycle.profiler.internal.DefaultTimer
枚举实现意味着在运行时不需要类路径扫描来找到它们,这使 Maven 的启动时间保持很快。请注意,我们的容器默认配置为仅使用索引。虽然这可以保持速度,但如果您在不包含索引的依赖项中使用 JSR-330 组件,则当前不会发现这些实现。这是一个合理的折衷方案,因为 Maven 是一个命令行工具,其中启动速度很重要。
如何在扩展中使用 JSR-330
让我们看一个示例扩展。我们将看一下 POM 和一些实现,以便您了解 JSR-330 扩展是如何工作的。实际上,它只是一个简单的 JSR-330 组件。如果您想查看完整的实现,可以在 Github 上找到它。
好的,让我们看一下POM:
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.maven</groupId>
<artifactId>maven-profiler</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Maven :: Profiler</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.sisu</groupId>
<artifactId>sisu-maven-plugin</artifactId>
<version>0.3.3</version>
<executions>
<execution>
<id>generate-index</id>
<goals>
<goal>main-index</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
因此,如前所述,我们有javax.inject
依赖项和sisu-maven-plugin
配置来创建 JSR-330 组件索引。当您构建扩展 JAR 并将其放置在${MAVEN_HOME}/lib/ext
文件夹中时,它将自动被 Maven 拾取。在这个例子中,我们有一个实现,EventSpy
它对生命周期中一个阶段内单个 mojo 的执行次数进行计时。
package org.apache.maven.lifecycle.profiler;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.maven.eventspy.AbstractEventSpy;
import org.apache.maven.execution.ExecutionEvent;
@Named
@Singleton
public class LifecycleProfiler extends AbstractEventSpy {
//
// Components
//
private SessionProfileRenderer sessionProfileRenderer;
//
// Profile data
//
private SessionProfile sessionProfile;
private ProjectProfile projectProfile;
private PhaseProfile phaseProfile;
private MojoProfile mojoProfile;
@Inject
public LifecycleProfiler(SessionProfileRenderer sessionProfileRenderer) {
this.sessionProfileRenderer = sessionProfileRenderer;
}
@Override
public void init(Context context) throws Exception {
}
@Override
public void onEvent(Object event) throws Exception {
if (event instanceof ExecutionEvent) {
ExecutionEvent executionEvent = (ExecutionEvent) event;
if (executionEvent.getType() == ExecutionEvent.Type.SessionStarted) {
//
//
//
sessionProfile = new SessionProfile();
} else if (executionEvent.getType() == ExecutionEvent.Type.SessionEnded) {
//
//
//
sessionProfile.stop();
sessionProfileRenderer.render(sessionProfile);
} else if (executionEvent.getType() == ExecutionEvent.Type.ProjectStarted) {
//
// We need to collect the mojoExecutions within each project
//
projectProfile = new ProjectProfile(executionEvent.getProject());
} else if (executionEvent.getType() == ExecutionEvent.Type.ProjectSucceeded || executionEvent.getType() == ExecutionEvent.Type.ProjectFailed) {
//
//
//
projectProfile.stop();
sessionProfile.addProjectProfile(projectProfile);
} else if (executionEvent.getType() == ExecutionEvent.Type.MojoStarted) {
String phase = executionEvent.getMojoExecution().getLifecyclePhase();
//
// Create a new phase profile if one doesn't exist or the phase has changed.
//
if(phaseProfile == null) {
phaseProfile = new PhaseProfile(phase);
} else if (!phaseProfile.getPhase().equals(phase)) {
phaseProfile.stop();
projectProfile.addPhaseProfile(phaseProfile);
phaseProfile = new PhaseProfile(phase);
}
mojoProfile = new MojoProfile(executionEvent.getMojoExecution());
} else if (executionEvent.getType() == ExecutionEvent.Type.MojoSucceeded || executionEvent.getType() == ExecutionEvent.Type.MojoFailed) {
//
//
//
mojoProfile.stop();
phaseProfile.addMojoProfile(mojoProfile);
}
}
}
}
如何在插件中使用 JSR-330
让我们看一个示例插件。POM 的设置方式与扩展类似,但我们在示例中添加了一个依赖项来扩展maven-plugin-api
和使用 Java 5 插件注释。maven-plugin-annotations
AbstractMojo
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jsr330-plugin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<name>maven-jsr330-plugin Maven Plugin</name>
<url>https://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mavenVersion>3.0.4</mavenVersion>
<mavenPluginPluginVersion>3.2</mavenPluginPluginVersion>
</properties>
<dependencies>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${mavenVersion}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>${mavenPluginPluginVersion}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>${mavenPluginPluginVersion}</version>
<configuration>
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
<execution>
<id>help-goal</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eclipse.sisu</groupId>
<artifactId>sisu-maven-plugin</artifactId>
<version>0.3.3</version>
<executions>
<execution>
<id>generate-index</id>
<goals>
<goal>main-index</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
现在让我们看一下插件代码。您会注意到我们正在使用构造函数注入,这使测试变得更加容易。如果你想测试你的Jsr330Component
,你不需要容器来实例化Mojo
. 在这个简单的例子中,您实际上可以在不使用插件测试工具的情况下测试此插件,因为您可以直接实例化Jsr330Component
andJsr330Mojo
并使用构造函数手动连接所有内容。Plexus 所缺乏的构造函数注入极大地简化了测试。
package org.apache.maven.plugins;
import javax.inject.Inject;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
@Mojo( name = "hello", defaultPhase = LifecyclePhase.VALIDATE, requiresProject = false )
public class Jsr330Mojo
extends AbstractMojo
{
private Jsr330Component component;
@Inject
public Jsr330Mojo( Jsr330Component component )
{
this.component = component;
}
public void execute()
throws MojoExecutionException
{
//
// Say hello to the world, my little constructor injected component!
//
component.hello();
}
}
如果您想查看这个示例项目,您可以在 Maven Core ITs中找到代码。