如何在几个Maven项目之间共享公共属性?

我有几个项目由maven构build,我想分享一些共同的属性 – spring的版本,MySQL的驱动程序版本,SVN基地url等 – 所以我可以更新一次,它会反映在所有项目。

我想有一个超级POM的所有属性,但如果我改变了一个问题,我需要增加它的版本(并更新所有的POM从它inheritance),或从所有开发人员的机器删除它我不想这样做。

可以在pom外部指定这些参数吗? 我仍然想在父pom中有外部位置定义。

请注意,我在这里的最初想法是我正在做的事情,但是我可能已经find了一个更好的想法,我已经在下面列出了。 为了完整性,我想在这里保留这两个想法,以防新的想法不起作用。


我想你可以使用父pom解决这个问题,但是你需要有一个maven仓库和一个CI构build工具。

我有几个项目都从父POMinheritance基本属性。 我们使用Java 1.5,所以build立属性是在那里设置的。 一切都是UTF-8。 所有我希望运行的报告,Sonar设置等都在父POM中。

假设你的项目是版本控制,并且你有一个CI工具,当你签入时,你的CI工具可以构build到POM项目并将SNAPSHOT部署到maven仓库。 如果您的项目指向父POM的SNAPSHOT版本,他们将检查存储库以validation它们是否具有最新版本…如果不是,则下载最新版本。 所以如果你更新父项,所有其他项目都会更新。

诀窍,我想是用SNAPSHOT发布。 我会说你的发布会比你的改变less得多。 所以你执行一个你的POM的版本,然后更新你的POM并inheritance它们,并将它们检入到版本控制中。 让开发人员知道,他们需要做一个更新,并从那里去。

你可以在那里触发构build,迫使新的POM进入存储库,然后让所有的开发者在构build时自动获取这些变化。


我已经删除了最新的/发布的关键字的想法,因为它们不适用于父POM。 他们只为依赖或插件工作。 问题区域在DefaultMavenProjectBuilder中。 从本质上讲,确定哪个存储库查找父级以确定最新版本或发行版本是困难的。 不知道为什么这是不同的依赖或插件。


听起来像这样比每次更改父POM时更新POM都要痛苦不less。

你可以做的是使用属性Maven插件 。 这将允许您在外部文件中定义您的属性,并且插件将读取此文件。

有了这个configuration:

 <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>properties-maven-plugin</artifactId> <version>1.0-alpha-1</version> <executions> <execution> <phase>initialize</phase> <goals> <goal>read-project-properties</goal> </goals> <configuration> <files> <file>my-file.properties</file> </files> </configuration> </execution> </executions> </plugin> </plugins> </build> 

如果你有,在你的属性文件中有以下几行:

 spring-version=1.0 mysql-version=4.0.0 

那么就和你在你的pom.xml中写下如下代码一样:

 <properties> <spring-version>1.0</spring-version> <mysql-version>4.0.0</mysql-version> </properties> 

使用这个插件,你将有几个好处:

  • 轻松设置一长串属性
  • 修改这些属性的值,而不修改父pom.xml。

我认为properties-maven-plugin是长期的正确方法,但是当你回答这个答案时,它不允许属性被inheritance。 在maven-shared-io中有一些工具可以让你发现项目类path上的资源。 我在下面包含了一些代码,它扩展了属性插件,以在插件的依赖关系中查找属性文件。

configuration声明了一个属性文件的path,因为描述符项目是在插件configuration中声明的,可以通过ClasspathResourceLocatorStrategy访问。 configuration可以在父项目中定义,并将由所有子项目inheritance(如果这样做,避免声明任何文件,因为它们不会被发现,只设置filePaths属性)。

下面的configuration假定有另一个名为name.seller.rich:test-properties-descriptor:0.0.1的jar项目,它有一个名为external.properties的文件打包到jar中(即在src / main / resources中定义) 。

 <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>properties-ext-maven-plugin</artifactId> <version>0.0.1</version> <executions> <execution> <id>read-properties</id> <phase>initialize</phase> <goals> <goal>read-project-properties</goal> </goals> </execution> </executions> <configuration> <filePaths> <filePath>external.properties</filePath> </filePaths> </configuration> <dependencies> <!-- declare any jars that host the required properties files here --> <dependency> <groupId>name.seller.rich</groupId> <artifactId>test-properties-descriptor</artifactId> <version>0.0.1</version> </dependency> </dependencies> </plugin> 

插件项目的pom如下所示:

 <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 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.codehaus.mojo</groupId> <artifactId>properties-ext-maven-plugin</artifactId> <packaging>maven-plugin</packaging> <version>0.0.1</version> <dependencies> <dependency> <groupId>org.codehaus.mojo</groupId> <artifactId>properties-maven-plugin</artifactId> <version>1.0-alpha-1</version> </dependency> <dependency> <groupId>org.apache.maven.shared</groupId> <artifactId>maven-shared-io</artifactId> <version>1.1</version> </dependency> </dependencies> </project> 

mojo是属性插件的ReadPropertiesMojo的副本,具有额外的“filePaths”属性,允许您在类path中定义外部属性文件的相对path,它使文件属性为可选,并添加readPropertyFiles()和getLocation ()方法来定位文件,并在继续之前将任何filePaths合并到文件数组中。 我已经评论了我的修改,使其更清晰。

 package org.codehaus.mojo.xml; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Properties; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.apache.maven.shared.io.location.ClasspathResourceLocatorStrategy; import org.apache.maven.shared.io.location.FileLocatorStrategy; import org.apache.maven.shared.io.location.Location; import org.apache.maven.shared.io.location.Locator; import org.apache.maven.shared.io.location.LocatorStrategy; import org.apache.maven.shared.io.location.URLLocatorStrategy; import org.codehaus.plexus.util.cli.CommandLineUtils; /** * The read-project-properties goal reads property files and stores the * properties as project properties. It serves as an alternate to specifying * properties in pom.xml. * * @author <a href="mailto:zarars@gmail.com">Zarar Siddiqi</a> * @author <a href="mailto:Krystian.Nowak@gmail.com">Krystian Nowak</a> * @version $Id: ReadPropertiesMojo.java 8861 2009-01-21 15:35:38Z pgier $ * @goal read-project-properties */ public class ReadPropertiesMojo extends AbstractMojo { /** * @parameter default-value="${project}" * @required * @readonly */ private MavenProject project; /** * The properties files that will be used when reading properties. * RS: made optional to avoid issue for inherited plugins * @parameter */ private File[] files; //Begin: RS addition /** * Optional paths to properties files to be used. * * @parameter */ private String[] filePaths; //End: RS addition /** * If the plugin should be quiet if any of the files was not found * * @parameter default-value="false" */ private boolean quiet; public void execute() throws MojoExecutionException { //Begin: RS addition readPropertyFiles(); //End: RS addition Properties projectProperties = new Properties(); for (int i = 0; i < files.length; i++) { File file = files[i]; if (file.exists()) { try { getLog().debug("Loading property file: " + file); FileInputStream stream = new FileInputStream(file); projectProperties = project.getProperties(); try { projectProperties.load(stream); } finally { if (stream != null) { stream.close(); } } } catch (IOException e) { throw new MojoExecutionException( "Error reading properties file " + file.getAbsolutePath(), e); } } else { if (quiet) { getLog().warn( "Ignoring missing properties file: " + file.getAbsolutePath()); } else { throw new MojoExecutionException( "Properties file not found: " + file.getAbsolutePath()); } } } boolean useEnvVariables = false; for (Enumeration n = projectProperties.propertyNames(); n .hasMoreElements();) { String k = (String) n.nextElement(); String p = (String) projectProperties.get(k); if (p.indexOf("${env.") != -1) { useEnvVariables = true; break; } } Properties environment = null; if (useEnvVariables) { try { environment = CommandLineUtils.getSystemEnvVars(); } catch (IOException e) { throw new MojoExecutionException( "Error getting system envorinment variables: ", e); } } for (Enumeration n = projectProperties.propertyNames(); n .hasMoreElements();) { String k = (String) n.nextElement(); projectProperties.setProperty(k, getPropertyValue(k, projectProperties, environment)); } } //Begin: RS addition /** * Obtain the file from the local project or the classpath * * @throws MojoExecutionException */ private void readPropertyFiles() throws MojoExecutionException { if (filePaths != null && filePaths.length > 0) { File[] allFiles; int offset = 0; if (files != null && files.length != 0) { allFiles = new File[files.length + filePaths.length]; System.arraycopy(files, 0, allFiles, 0, files.length); offset = files.length; } else { allFiles = new File[filePaths.length]; } for (int i = 0; i < filePaths.length; i++) { Location location = getLocation(filePaths[i], project); try { allFiles[offset + i] = location.getFile(); } catch (IOException e) { throw new MojoExecutionException( "unable to open properties file", e); } } // replace the original array with the merged results files = allFiles; } else if (files == null || files.length == 0) { throw new MojoExecutionException( "no files or filePaths defined, one or both must be specified"); } } //End: RS addition /** * Retrieves a property value, replacing values like ${token} using the * Properties to look them up. Shamelessly adapted from: * http://maven.apache. * org/plugins/maven-war-plugin/xref/org/apache/maven/plugin * /war/PropertyUtils.html * * It will leave unresolved properties alone, trying for System properties, * and environment variables and implements reparsing (in the case that the * value of a property contains a key), and will not loop endlessly on a * pair like test = ${test} * * @param k * property key * @param p * project properties * @param environment * environment variables * @return resolved property value */ private String getPropertyValue(String k, Properties p, Properties environment) { String v = p.getProperty(k); String ret = ""; int idx, idx2; while ((idx = v.indexOf("${")) >= 0) { // append prefix to result ret += v.substring(0, idx); // strip prefix from original v = v.substring(idx + 2); idx2 = v.indexOf("}"); // if no matching } then bail if (idx2 < 0) { break; } // strip out the key and resolve it // resolve the key/value for the ${statement} String nk = v.substring(0, idx2); v = v.substring(idx2 + 1); String nv = p.getProperty(nk); // try global environment if (nv == null) { nv = System.getProperty(nk); } // try environment variable if (nv == null && nk.startsWith("env.") && environment != null) { nv = environment.getProperty(nk.substring(4)); } // if the key cannot be resolved, // leave it alone ( and don't parse again ) // else prefix the original string with the // resolved property ( so it can be parsed further ) // taking recursion into account. if (nv == null || nv.equals(nk)) { ret += "${" + nk + "}"; } else { v = nv + v; } } return ret + v; } //Begin: RS addition /** * Use various strategies to discover the file. */ public Location getLocation(String path, MavenProject project) { LocatorStrategy classpathStrategy = new ClasspathResourceLocatorStrategy(); List strategies = new ArrayList(); strategies.add(classpathStrategy); strategies.add(new FileLocatorStrategy()); strategies.add(new URLLocatorStrategy()); List refStrategies = new ArrayList(); refStrategies.add(classpathStrategy); Locator locator = new Locator(); locator.setStrategies(strategies); Location location = locator.resolve(path); return location; } //End: RS addition }