动态修改Maven和Gradle依赖版本

说明

有个这样的场景,提供出去一些列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>
<!--        &lt;!&ndash; 设置jre版本为 1.8 &ndash;&gt;-->
        <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'

这两个有什么异同吗 ?

  1. plugins 是后来添加的特性。
  2. plugins 中指定的插件必须是 https://plugins.gradle.org/ 存在的。
  3. apply plugin 可以用在 allprojects 和 subprojects 中。
    所以,自定义的插件只能通过apply来使用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343