说明
有个这样的场景,提供出去一些列jar包,可能会不断的修改内容,需要升级版本,但是不想让各个使用方都去升级,有些背景导致不能使用SNAPSHOT版本(SNAPSHOT不能拉取到最新的包)
编写插件和扩展来实现
达到的目的是,使用方只需要使用插件,和指定大版本即可,至于引用包的小版本可以动态的修改,使用方只需要重新部署(Reload)就可以使用
Gradle实现
编写gradle插件,使用方引入插件即可
- 插件gradle配置
plugins {
id 'groovy'
id 'maven-publish'
id 'maven'
}
group 'com.demo.xxx'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
//使用 groovy 和 gradle 依赖
dependencies {
compile gradleApi()
compile localGroovy()
}
- 在resources下创建META-INF/gradle-plugins目录,添加test.dependency-management.properties文件(其中test.dependency-management就是插件的名称)test.dependency-management.properties文件内容为:
implementation-class=com.xxx.xxx.TestPlugin #plugin的实现
- 编写plugin,使用groove
class TestPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println("plugin version start handle version")
//创建变量
project.extensions.create("testVersion", VersionExtension)
//解析配置添加版本
project.configurations.all { configuration ->
configuration.getResolutionStrategy().eachDependency(new Action<DependencyResolveDetails>() {
@Override
void execute(DependencyResolveDetails details) {
//没有配置版本走默认版本
String globalVersion = getConfigVersion(project);
String group = details.getRequested().getGroup()
String name = details.getRequested().getName()
String version = details.getRequested().getVersion()
//判断是不是要修改的包的版本
if (VersionUtil.isManagerVersion(group, name, version, globalVersion)) {
//替换版本
def managerVersion = VersionUtil.getManagerVersion(name, globalVersion);
details.useVersion(managerVersion)
}
}
})
//upload的和install的时候使用
configurePomCustomization(project);
}
}
//获取用户配置的主版本,通过这个主版本找到各个引用jar包的版本
static String getConfigVersion(Project project) {
String globalVersion = project.testVersion.version
//没有配置版本走默认版本
globalVersion = (globalVersion == null || globalVersion == '') ? VersionInfo.DEFAULT_VERSION : globalVersion;
return globalVersion;
}
def configurePomCustomization(Project project) {
def handler = new TestPomHandler(getConfigVersion(project));
project.getTasks().withType(Upload, new Action<Upload>() {
@Override
void execute(final Upload upload) {
upload.doFirst(new Action<Task>() {
@Override
void execute(Task task) {
upload.getRepositories().withType(MavenResolver.class, new Action<MavenResolver>() {
@Override
void execute(MavenResolver mavenResolver) {
mavenResolver.getPom().withXml(handler);
}
});
}
});
}
});
project.getPlugins().withType(MavenPublishPlugin.class, new Action<MavenPublishPlugin>() {
@Override
void execute(MavenPublishPlugin mavenPublishPlugin) {
configurePublishingExtension(project, handler);
}
}
);
}
def configurePublishingExtension(Project project, TestPomHandler handler) {
project.getExtensions().configure(PublishingExtension.class, new Action<PublishingExtension>() {
@Override
void execute(PublishingExtension publishingExtension) {
configurePublications(publishingExtension, handler);
}
});
}
def configurePublications(PublishingExtension publishingExtension, TestPomHandler handler) {
publishingExtension.getPublications().withType(MavenPublication.class, new Action<MavenPublication>() {
@Override
void execute(MavenPublication mavenPublication) {
mavenPublication.getPom().withXml(handler);
}
});
}
}
class TestPomHandler implements Action<XmlProvider> {
private static final String NODE_NAME_DEPENDENCY_MANAGEMENT = "dependencyManagement";
private static final String NODE_NAME_DEPENDENCIES = "dependencies";
private static final String NODE_NAME_DEPENDENCY = "dependency";
private static final String NODE_NAME_GROUP_ID = "groupId";
private static final String NODE_NAME_ARTIFACT_ID = "artifactId";
private static final String NODE_NAME_VERSION = "version";
private String version;
TestPomHandler(String version) {
this.version = version;
}
@Override
public void execute(XmlProvider xmlProvider) {
if (!VersionUtil.VERSION_MAP.containsKey(version)) {
return
}
Node pom = xmlProvider.asNode();
Node dependencyManagementNode = findChild(pom, NODE_NAME_DEPENDENCY_MANAGEMENT);
if (dependencyManagementNode == null) {
dependencyManagementNode = pom.appendNode(NODE_NAME_DEPENDENCY_MANAGEMENT);
}
Node managedDependenciesNode = findChild(dependencyManagementNode, NODE_NAME_DEPENDENCIES);
if (managedDependenciesNode == null) {
managedDependenciesNode = dependencyManagementNode.appendNode(NODE_NAME_DEPENDENCIES);
}
configureImports(managedDependenciesNode)
}
Node findChild(Node node, String name) {
for (Object childObject : node.children()) {
if ((childObject instanceof Node) && ((Node) childObject).name().equals(name)) {
return (Node) childObject
}
}
return null;
}
void configureImports(Node parent) {
def versionMap = VersionUtil.VERSION_MAP.get(version)
versionMap.forEach({ name, version ->
Node dependencyNode = parent.appendNode(NODE_NAME_DEPENDENCY);
dependencyNode.appendNode(NODE_NAME_GROUP_ID, VersionInfo.DEFAULT_GROUP)
dependencyNode.appendNode(NODE_NAME_ARTIFACT_ID, name)
dependencyNode.appendNode(NODE_NAME_VERSION, version)
})
}
}
VersionUtil和VersionInfo就不贴了,大致逻辑就是判断GAV是不是要替换版本的包,是的话就从git上拿到配置的版本进行替换
- 使用方依赖
buildscript {
//配置仓库地址
repositories {
mavenLocal()
mavenCentral()
}
//引入插件依赖
dependencies {
classpath("com.demo.xxx:xxx-plugin:1.0-SNAPSHOT")
}
}
//使用插件
apply plugin: 'test.dependency-management'
//指明使用的大版本
testVersion {
version = '1.0'
}
//引入框架starter,不需要配置版本
dependencies {
compile "com.xxx.xxx:xxxJar1"
compile "com.xxx.xxx:xxxJar2"
//其他包正常依赖
compile "org.codehaus.plexus:plexus-component-annotations:1.5"
}
Maven实现
- 扩展的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>com.demo.xxx</groupId>
<artifactId>test-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<!-- 设置编码为 UTF-8 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<!-- <!– 设置jre版本为 1.8 –>-->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-annotations</artifactId>
<version>1.5.5</version>
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.6.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-component-metadata</artifactId>
<version>1.5.5</version>
<executions>
<execution>
<goals>
<goal>generate-metadata</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- 扩展的实现
@Component(role = AbstractMavenLifecycleParticipant.class, hint = "test")
public class TestLifecycle extends AbstractMavenLifecycleParticipant {
@Requirement
private Logger logger;
//在开始的时候会调用,idea的relod,和maven的各个阶段都会调用
@Override
public void afterSessionStart(final MavenSession session) throws MavenExecutionException {
logger.info("test life cycle start");
super.afterSessionStart(session);
}
//在结束的时候会调用
@Override
public void afterSessionEnd(MavenSession session) throws MavenExecutionException {
logger.info("test life cycle end");
VersionUtil.reset();
super.afterSessionEnd(session);
}
}
- 重写DefaultModelProcessor,Maven在解析pom文件时会使用,只是用来解析pom中配置的大版本配置,支持在parent中配置大版本
@Component(role = ModelProcessor.class)
public class TestProcessor extends DefaultModelProcessor {
@Requirement
private Logger logger;
@Override
public Model read(File input, Map<String, ?> options) throws IOException {
Model model = super.read(input, options);
getPropertiesVersionConfig(model);
return model;
}
@Override
public Model read(Reader input, Map<String, ?> options) throws IOException {
Model model = super.read(input, options);
getPropertiesVersionConfig(model);
return model;
}
@Override
public Model read(InputStream input, Map<String, ?> options) throws IOException {
Model model = super.read(input, options);
getPropertiesVersionConfig(model);
return model;
}
private void getPropertiesVersionConfig(Model model) {
if (VersionUtil.getGlobalVersion() != null && !"".equals(VersionUtil.getGlobalVersion())) {
return;
}
if (model.getProperties() == null || !model.getProperties().containsKey(VersionInfo.VERSION_CONFIG_KEY)) {
return;
}
String globalVersion = model.getProperties().get(VersionInfo.VERSION_CONFIG_KEY).toString();
VersionUtil.setGlobalVersion(globalVersion);
}
}
- 重写ModelBuilder,在maven在解析pom调用
@Component(role = ModelBuilder.class)
public class TestModelBuilder extends DefaultModelBuilder {
@Requirement
private Logger logger;
@Override
public ModelBuildingResult build(ModelBuildingRequest request) throws ModelBuildingException {
ModelBuildingResult buildingResult = null;
try {
buildingResult = super.build(request);
versionBinding(buildingResult);
} catch (Exception e) {
throw e;
}
return buildingResult;
}
//重写版本
private void versionBinding(ModelBuildingResult buildingResult) {
if (buildingResult == null) {
return;
}
Model effectiveModel = buildingResult.getEffectiveModel();
if (effectiveModel == null) {
return;
}
List<Dependency> dependencies = effectiveModel.getDependencies();
if (dependencies == null || dependencies.size() == 0) {
return;
}
for (Dependency dependency : dependencies) {
if (dependency.getVersion() != null) {
continue;
}
if (!VersionUtil.isManagerVersion(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion())) {
continue;
}
dependency.setVersion(VersionUtil.getManagerVersion(dependency.getArtifactId()));
}
}
@Override
public ModelBuildingResult build(ModelBuildingRequest request, ModelBuildingResult result) throws ModelBuildingException {
ModelBuildingResult buildingResult = null;
try {
//注意,前后都需要调用
versionBinding(result);
buildingResult = super.build(request, result);
versionBinding(buildingResult);
} catch (Exception e) {
throw e;
}
return buildingResult;
}
}
- 重写FileProcessor,Maven在install的时候调用,将文件复制到到本地或者私服
@Component(
role = FileProcessor.class
)
public class InstallFilterProcessor extends DefaultFileProcessor {
@Requirement
private Logger logger;
@Override
public long copy(File source, File target, ProgressListener listener) throws IOException {
long total = 0L;
InputStream in = null;
FileOutputStream out = null;
try {
try {
//解析pom文件增加version标签
in = appendVersion(source);
} catch (Exception e) {
logger.warn("cast file error:" + e.getMessage());
}
in = in == null ? new FileInputStream(source) : in;
this.mkdirs(target.getAbsoluteFile().getParentFile());
out = new FileOutputStream(target);
total = this.copy((OutputStream) out, (InputStream) in, listener);
out.close();
out = null;
in.close();
in = null;
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException var43) {
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException var42) {
}
}
}
return total;
}
}
-- 重写DefaultRepositorySystemSessionFactory,Maven在upload的时候调用,将文件复制到到私服
@Component(role = DefaultRepositorySystemSessionFactory.class)
public class TestRepositorySystemSessionFactory extends DefaultRepositorySystemSessionFactory {
@Requirement
private Logger logger;
@Override
public DefaultRepositorySystemSession newRepositorySession(MavenExecutionRequest request) {
DefaultRepositorySystemSession defaultRepositorySystemSession = super.newRepositorySession(request);
FileTransformerManager fileTransformerManager = defaultRepositorySystemSession.getFileTransformerManager();
if (fileTransformerManager instanceof TestFileTransformerManager) {
return defaultRepositorySystemSession;
}
logger.debug("reConfig fileTransformerManager set TestFileTransformerManager");
defaultRepositorySystemSession.setFileTransformerManager(new TestFileTransformerManager(fileTransformerManager, logger));
return defaultRepositorySystemSession;
}
static class TestFileTransformerManager implements FileTransformerManager {
private FileTransformerManager fileTransformerManager;
private Logger logger;
public TestFileTransformerManager(FileTransformerManager fileTransformerManager, Logger logger) {
this.fileTransformerManager = fileTransformerManager;
this.logger = logger;
}
@Override
public Collection<FileTransformer> getTransformersForArtifact(Artifact artifact) {
if (fileTransformerManager != null) {
Collection<FileTransformer> transformersForArtifact = fileTransformerManager.getTransformersForArtifact(artifact);
if (transformersForArtifact != null && transformersForArtifact.size() > 0) {
return transformersForArtifact;
}
}
return Collections.singleton(new FileTransformer() {
@Override
public Artifact transformArtifact(Artifact artifact) {
return artifact;
}
@Override
public InputStream transformData(File file) throws IOException, TransformException {
try {
logger.debug("start rewrite pom version" + file.getName());
//将install中的方法抽取
InputStream inputStream = FileUtil.fileAddVersionToStream(file, logger);
if (inputStream != null) {
logger.info("rewrite pom version : " + file.getName());
return inputStream;
}
} catch (Exception ignore) {
logger.debug("rewrite file: " + file.getName() + " version error,", ignore);
}
return new FileInputStream(file);
}
});
}
}
}
- 使用方接入
//配置使用的大版本
<properties>
<project.test.version>1.0</project.test.version>
</properties>
在pom.xml文件在的文件夹下创建,.mvn文件夹,在.mvn下新增extensions.xml,extensions.xml文件内容如下:
<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
<extension>
<groupId>com.demo.xxxx</groupId>
<artifactId>test-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
</extension>
</extensions>
<dependencies>
<dependency>
<groupId>com.xxx.xxx.group</groupId>
<artifactId>com.xxx.xxx.jar1</artifactId>
</dependency>
<dependency>
<groupId>com.xxx.xxx.group</groupId>
<artifactId>om.xxx.xxx.jar2</artifactId>
</dependency>
//其他包正常依赖
<dependency>
<groupId>org.example</groupId>
<artifactId>version-demo3</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
maven每个阶段都是调用插件,可以查看MAVEN的源码和插件的源码来进行定制话的扩展,上面就是重写了容器中的一些类,插件在引入是会引入自定义的类
参考地址:
mavenPlugin:https://maven.apache.org/plugins/index.html
extension编写:https://maven.apache.org/examples/maven-3-lifecycle-extensions.html
环境说明:
Maven使用3.6+,gradle使用4.10.3
注意:
在使用MAVEN的是时候,IDEA可能会报错,原因是IDEA有缓存,可以通过打日志,同时查看IDEA的日志来查看是否使用的新的扩展,IDEA日志目录地址:
help->show log in finder
在使用过程中可能会发现,在修改大版本后,包依赖的版本可能没有更新,也是IDEA缓存的问题,需要重新打开项目
plugins {
id 'java'
}
apply plugin: 'java'
这两个有什么异同吗 ?
- plugins 是后来添加的特性。
- plugins 中指定的插件必须是 https://plugins.gradle.org/ 存在的。
- apply plugin 可以用在 allprojects 和 subprojects 中。
所以,自定义的插件只能通过apply来使用