Gradle系列(3)——Gradle extension(gradle扩展),如何自定义gradle扩展,AppPlugin,AppExtension原理


系列: Gradle系列(2)——如何自定以Gradle 插件

在上一篇Gradle系列(2)——如何自定以Gradle 插件 中,我们了解了什么是 Gradle Plugin以及如何用Gradle Plugin在项目构建中实现更多强力而有趣的功能。尽管在demo中我们已经实现了在整个公司的不同团队通过本地json文件实现平台化的配置,但是仍然存在某个项目需要个性化的配置,或者在构建过程中需要一些灵活便捷的自定义的构建,那么上一篇实现的功能就不能满足我们的需求,于是本篇就引入了如何自定义Gradle Extensions


1.什么是Extensions

  • 我们其实经常接触Gradle Extension

Gradle Extensions 是Gradle Plugin 的一种扩展机制,通过实现自定义的Extension,我们可以在Gradle 脚本(例如build.gradle)中增加自己的配置,例如android(其实例为com.android.build.gradle.internal.dsl.BaseAppModuleExtensioncom.android.build.gradle.LibraryExtension),
Gradle在configuration阶段可以读取这些配置里面的内容。

2.如何自定义Extension

关于这点,百度搜索有许多好多大量海量千篇一律毫无区别的帖子告诉你怎么定义一个自定义扩展并注册,我不再展开,就按照他们写的简单写个小demo

  • 定义一个扩展

public class TestExtension {

    public String anyString;
}

  • 在Plugin实现类注册这个扩展

public class RocketPlugin implements Plugin<Project>  {

    @Override
    public void apply(Project project) {
        super.apply(project);
        project.getExtensions().create("anyTest", TestExtension.class);
    }
}


我们给build.gradle内注册了一个TestExtension的实例,名字为anyTest

  • 使用这个扩展
    在app/library build.gradle内
plugins {
    id("com.android.application")
    id("gradle-rocket")
}

anyTest{
    topic = "extension test"
}

  • 获取插件的配置信息

在Plugin实现类内:


public class RocketPlugin implements Plugin<Project>  {

    @Override
    public void apply(Project project) {
        super.apply(project);
        TestExtension anyTest = project.getExtensions().create("anyTest", TestExtension.class);
          project.afterEvaluate(new Action<Project>() {
            @Override
            public void execute(Project project) {
                System.out.println("    afterEvaluate :anyTest:"+anyTest.topic);
            }
        });
    }
}


然后同步就可以看到输出:

!请添加图片描述

按照大家的说法,到这一步我们实现了一个简单的扩展。

3.问题来了——如何通过自定义Extension给Plugin传递数据

到上一节,我们实现了一个扩展,简单说明如何定义注册使用扩展,但,这只是一个简陋的扩展,甚至不能称之为扩展,我称之为扩展都感觉十分脸红惭愧甚至觉得丢人。

回想看过的十数篇帖子,不由得内心怒骂好几次:

这TM是扩展?!!这TM是扩展?!!这TM是扩展?!!

你们这些灌水的人给我翻译翻译,什么TM的叫TM的扩展?!!

为什么这个东西不能叫扩展?

我们回到开头的“什么是Extensions”,扩展的核心诉求是在gradle构建脚本(比如build.gradle)中增加我们自己的配置应用到我们的自定义gradle plugin中。而在以上篇章我们的扩展在哪里应用了,可以看到一行代码:

   estExtension anyTest = project.getExtensions().create("anyTest", TestExtension.class);

   project.afterEvaluate(new Action<Project>() {
            @Override
            public void execute(Project project) {
                System.out.println("    afterEvaluate :anyTest:"+anyTest.topic);
            }
        });
        //外部直接打印
      System.out.println("    afterEvaluate :anyTest:"+anyTest.topic);


我们注意project.afterEvaluate这个方法,在我的上一篇Gradle系列(2)——如何自定以Gradle 插件 中我简单讲了gradle的生命周期以及监听,afterEvaluate表示,gradle 的configuration阶段已经完成了。而上述代码表示,在项目配置完成后,输出一下我们扩展配置的值

  • 实际问题

由于我们做的是平台化项目,androidharmony双平台打包的,两个平台应用的三方库,签名机制也不一样,所以我需要知道编译的是哪个平台,是否需要支持双平台,所以我添加了一个扩展来配置这个信息,然而我在上述afterEvaluate生命周期去做签名的时候,发现报错(当时忘了截图,大概意思是说buildType已经创建好了,此时修改太晚了)

我们尝试在外部直接打印,你会发现,根本没有值!

那么问题来了——我们这里实现的扩展还有什么用呢?

答案是,没卵用!

你去搜索相关的帖子,千篇一律,都是这样,甚至你看官网,他们的demo也是这样!我请问一下,这样一个不能给插件传送数据,只能看没有实际用处的花架子,它叫扩展吗?

4.BaseAppModuleExtension和AppPlugin部分原理

那么到底在构建脚本怎么通过我们的扩展给插件传递数据呢?我想,我们可以看Android的AppExtension和AppPlugin是如何实现的呀。

注意:

  • com.android.application,com.android.library 实现类分别为com.android.build.gradle.internal.plugins.AppPlugincom.android.build.gradle.internal.plugins.LibraryPlugin

  • build.gradle中声明的“android”扩展的实例根据module是应用的插件不同,其实例可能是com.android.build.gradle.internal.dsl.BaseAppModuleExtensioncom.android.build.gradle.LibraryExtension

BuildTypes是如何创建并传递数据给AppPlugin的?

我们以build.gradle.kts中buildTypes为例:


plugins {
    id("com.android.application")
    id("gradle-rocket")
}
android {
	//。。。

        create("test") {
            isMinifyEnabled = false
            isDebuggable = true
            signingConfigs { }
        }

	//。。。
}

其实build.gradle的dsl写法我们可以理解为一种极致的lambda,buildTypes完整写法应该是


    buildTypes {

        create("test", object : Action<ApplicationBuildType> {

            override fun execute(t: ApplicationBuildType) {
                t.isMinifyEnabled = false
                t.isDebuggable = true
            }
        })
    }

我们点进去这个create方法,
在这里插入图片描述

可以看到,这是一个抽象类,点击左侧图标查看源码:

在这里插入图片描述

看得出来这个类应用还是比较广泛的,一时难以确定实现类是哪个。我们转变思路看AppPlugin:

AppPlugin是如何接收数据的?

查看AppPlugin源码


public class AppPlugin
        extends AbstractAppPlugin<
                com.android.build.api.dsl.ApplicationExtension,
                ApplicationAndroidComponentsExtension,
                ApplicationVariantBuilderImpl,
                ApplicationVariantImpl> {
    @Inject
    public AppPlugin(
            ToolingModelBuilderRegistry registry,
            SoftwareComponentFactory componentFactory,
            BuildEventsListenerRegistry listenerRegistry) {
        super(registry, componentFactory, listenerRegistry);
    }

    @NonNull
    @Override
    protected BaseExtension createExtension(
            @NonNull DslServices dslServices,
            @NonNull GlobalScope globalScope,
            @NonNull
                    DslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig>
                            dslContainers,
            @NonNull NamedDomainObjectContainer<BaseVariantOutput> buildOutputs,
            @NonNull ExtraModelInfo extraModelInfo) {
        ApplicationExtensionImpl applicationExtension =
                dslServices.newDecoratedInstance(ApplicationExtensionImpl.class, dslServices, 

          //..忽略一万行代码

          //创建“android”,注入到所有Extensions中
        return project.getExtensions()
                .create(
                        "android",
                        BaseAppModuleExtension.class,
                        dslServices,
                        globalScope,
                        buildOutputs,
                        dslContainers.getSourceSetManager(),
                        extraModelInfo,
                        applicationExtension);
    }

}

AppPluginLibraryPlugin代码都相当简单,主要区别在各自的泛型以及createExtensionandroid的实现类不同。逻辑都在其父类com.android.build.gradle.internal.plugins.BasePlugin,这个类也实现了Plugin接口,我们主要看apply方法


    @Override
    public final void apply(@NonNull Project project) {
        CrashReporting.runAction(
                () -> {
                    basePluginApply(project);
                    pluginSpecificApply(project);
                    project.getPluginManager().apply(AndroidBasePlugin.class);
                });
    }


接着进入basePluginApply


    private void basePluginApply(@NonNull Project project) {
        // We run by default in headless mode, so the JVM doesn't steal focus.
        System.setProperty("java.awt.headless", "true");

        this.project = project;

        //...
        
        configuratorService.recordBlock(
                ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,
                project.getPath(),
                null,
                this::configureProject);

        configuratorService.recordBlock(
                ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,
                project.getPath(),
                null,
                this::configureExtension);

        configuratorService.recordBlock(
                ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,
                project.getPath(),
                null,
                this::createTasks);
    }


这3方法都比较重要,但我们要看configureExtension


    private void configureExtension() {
        DslServices dslServices = globalScope.getDslServices();

        final NamedDomainObjectContainer<BaseVariantOutput> buildOutputs =
                project.container(BaseVariantOutput.class);

        project.getExtensions().add("buildOutputs", buildOutputs);

        //1.创建BuildType 工厂类
        variantFactory = createVariantFactory(projectServices, globalScope);

        variantInputModel =
                new LegacyVariantInputManager(
                        dslServices,
                        variantFactory.getVariantType(),
                        new SourceSetManager(
                                project,
                                isPackagePublished(),
                                dslServices,
                                new DelayedActionsExecutor()));
         //2.子类中重载的方法                       	
        extension =
                createExtension(
                        dslServices, globalScope, variantInputModel, buildOutputs, extraModelInfo);
        //3.把扩展添加到全局作用域       
        globalScope.setExtension(extension);

        androidComponentsExtension = createComponentExtension(dslServices, variantApiOperations);

        variantManager =
                new VariantManager(
                        globalScope,
                        project,
                        projectServices.getProjectOptions(),
                        extension,
                        variantApiOperations,
                        variantFactory,
                        variantInputModel,
                        projectServices);

        registerModels(
                registry,
                globalScope,
                variantInputModel,
                extension,
                extraModelInfo);

        // create default Objects, signingConfig first as its used by the BuildTypes.
        //创建默认的buildType和签名
        variantFactory.createDefaultComponents(variantInputModel);

        createAndroidTestUtilConfiguration();
    }


这段代码我们需要了解的有三处:

  • createVariantFactory创建了我们的buildTypes工厂
  • 调用熟悉的createExtension创建我们的扩展实例,就是在上面提到子类中重载的方法。
  • 把扩展实例添加到全局作用域
  • 创建默认的buildType和签名

那么我们接下来createVariantFactory是怎么创建的,继而看我们的BuildType到底怎么创建的。

    protected abstract VariantFactory<VariantBuilderT, VariantT> createVariantFactory(
            @NonNull ProjectServices projectServices, @NonNull GlobalScope globalScope);

这是个抽象方法,我们看实现类

在这里插入图片描述

能看到AppPlugin,这个类是BaseAppModuleExtension的父类


    @NonNull
    @Override
    protected ApplicationVariantFactory createVariantFactory(
            @NonNull ProjectServices projectServices, @NonNull GlobalScope globalScope) {
        return new ApplicationVariantFactory(projectServices, globalScope);
    }

最终到了ApplicationVariantFactory


class ApplicationVariantFactory(
    projectServices: ProjectServices,
    globalScope: GlobalScope
) : AbstractAppVariantFactory<ApplicationVariantBuilderImpl, ApplicationVariantImpl>(
    projectServices,
    globalScope
) {
	//。。。
}

这个类的父类是AbstractAppVariantFactory,我们先记住这个类,返回到configureExtension,查看variantFactory.createDefaultComponents(variantInputModel);

    fun createDefaultComponents(
            dslContainers: DslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig>)

这是VariantFactory接口的抽象方法,接着看实现类AbstractAppVariantFactory

    override fun createDefaultComponents(
            dslContainers: DslContainerProvider<DefaultConfig, BuildType, ProductFlavor, SigningConfig>) {
        // must create signing config first so that build type 'debug' can be initialized
        // with the debug signing config.
        //创建默认的debug签名
        dslContainers.signingConfigContainer.create(BuilderConstants.DEBUG)
        //创建默认的debug和release buildType
        dslContainers.buildTypeContainer.create(BuilderConstants.DEBUG)
        dslContainers.buildTypeContainer.create(BuilderConstants.RELEASE)
    }


这个方法里做了两件事:

  • 创建默认的debug签名
  • 创建默认的debug和releae buildType

这就解释了为什么我们项目为什么不配置签名也能安装,以及两个默认的buidType从何而来。(这段代码救了我的命,在做平台化Flavor和签名的时候要配置我们的签名密钥,创建SigningConfig遇到问题,就是看这段代码找到了解决方法)

好,我们接着往下看dslContainers.buildTypeContainer.create(BuilderConstants.DEBUG)

在这里插入图片描述

是不是回到了远点,又是NamedDomainObjectContainer

buildTypeContainer

那么我们的buildTypeContainer从何而来呢?

我们要看dslContainers;

createDefaultComponents(),从方法形参可以看到它的类型是DslContainerProvider

往回退到configureExtension(),可以看到dslContainers就是variantInputModel;在configureExtension开始的地方创建的:


        variantInputModel =
                new LegacyVariantInputManager(
                        dslServices,
                        variantFactory.getVariantType(),
                        new SourceSetManager(
                                project,
                                isPackagePublished(),
                                dslServices,
                                new DelayedActionsExecutor()));



我们再看这个LegacyVariantInputManager的代码:


class LegacyVariantInputManager(
    dslServices: DslServices,
    variantType: VariantType,
    sourceSetManager: SourceSetManager
) : AbstractVariantInputManager<DefaultConfig, BuildType, ProductFlavor, SigningConfig>(
    dslServices,
    variantType,
    sourceSetManager
) {

    override val buildTypeContainer: NamedDomainObjectContainer<BuildType> =
        dslServices.domainObjectContainer(
            BuildType::class.java, BuildTypeFactory(dslServices)
        )
    override val productFlavorContainer: NamedDomainObjectContainer<ProductFlavor> =
        dslServices.domainObjectContainer(
            ProductFlavor::class.java,
            ProductFlavorFactory(dslServices)
        )
    override val signingConfigContainer: NamedDomainObjectContainer<SigningConfig> =
        dslServices.domainObjectContainer(
            SigningConfig::class.java,
            SigningConfigFactory(
                dslServices,
                getBuildService(
                    dslServices.buildServiceRegistry,
                    AndroidLocationsBuildService::class.java
                ).get().getDefaultDebugKeystoreLocation()
            )
        )

    override val defaultConfig: DefaultConfig = dslServices.newInstance(
        DefaultConfig::class.java,
        BuilderConstants.MAIN,
        dslServices
    )
}

这里我特意多粘贴了点代码,是不是看着很熟悉,有productFlavorsigningConfigContainer, defaultConfig

我们接着看buildTypeContainer = dslServices.domainObjectContainer,它是接口DslServices的方法,唯一实现类DslServicesImpl


class DslServicesImpl constructor(
    projectServices: ProjectServices,
    override val sdkComponents: Provider<SdkComponentsBuildService>
) : BaseServicesImpl(projectServices), DslServices {



    override fun <T> domainObjectContainer(
        type: Class<T>,
        factory: NamedDomainObjectFactory<T>
    ): NamedDomainObjectContainer<T> =
        projectServices.objectFactory.domainObjectContainer(type, factory)

}

继续点进去domainObjectContainer,他是接口ObjectFactory的方法,它的有效实现类是DefaultObjectFactory,查看它的方法:


    @Override
    public <T> NamedDomainObjectContainer<T> domainObjectContainer(Class<T> elementType, NamedDomainObjectFactory<T> factory) {
        return domainObjectCollectionFactory.newNamedDomainObjectContainer(elementType, factory);
    }

继续点进去仍然是个接口DomainObjectCollectionFactory:

public interface DomainObjectCollectionFactory {
    <T> NamedDomainObjectContainer<T> newNamedDomainObjectContainer(Class<T> elementType, NamedDomainObjectFactory<T> factory);
}

接着看它唯一实现类DefaultDomainObjectCollectionFactory:

是不是很绕啊,确实这么绕,山高水远,道阻且长,说实话

BaePlugin


public class DefaultDomainObjectCollectionFactory implements DomainObjectCollectionFactory {

    @Override
    public <T> NamedDomainObjectContainer<T> newNamedDomainObjectContainer(Class<T> elementType, NamedDomainObjectFactory<T> factory) {
        Instantiator instantiator = instantiatorFactory.decorateLenient();
        return Cast.uncheckedCast(instantiator.newInstance(FactoryNamedDomainObjectContainer.class, elementType, instantiator, new DynamicPropertyNamer(), factory, mutationGuard, collectionCallbackActionDecorator));
    }


}

看到这里就没有必要继续往下看了,其实已经能看出来了,这里其实就是反射创建了FactoryNamedDomainObjectContainer的实例,那我们接着看它


public class FactoryNamedDomainObjectContainer<T> extends AbstractNamedDomainObjectContainer<T> {
    
    public FactoryNamedDomainObjectContainer(Class<T> type, Instantiator instantiator, Namer<? super T> namer, NamedDomainObjectFactory<T> factory, MutationGuard crossProjectConfiguratorMutationGuard, CollectionCallbackActionDecorator collectionCallbackActionDecorator) {
        super(type, instantiator, namer, collectionCallbackActionDecorator);
        this.factory = factory;
        this.crossProjectConfiguratorMutationGuard = crossProjectConfiguratorMutationGuard;
    }

    @Override
    protected T doCreate(String name) {
        return factory.create(name);
    }
}

最终,我们找到了dslContainers.buildTypeContainer的实现类NamedDomainObjectContainer

(这里我特意贴了构造函数和一个方法,先记住,然后往后看)

它并没有实现create方法,我们接着看它的父类


public abstract class AbstractNamedDomainObjectContainer<T> extends DefaultNamedDomainObjectSet<T> implements NamedDomainObjectContainer<T>, HasPublicType {
   
    @Override
    public T create(String name) {
        assertMutable("create(String)");
        return create(name, Actions.doNothing());
    }

        @Override
    public T create(String name, Closure configureClosure) {
        assertMutable("create(String, Closure)");
        return create(name, ConfigureUtil.configureUsing(configureClosure));
    }

    @Override
    public T create(String name, Action<? super T> configureAction) throws InvalidUserDataException {
        assertMutable("create(String, Action)");
        assertCanAdd(name);
        //创建BuildType实例
        T object = doCreate(name);
        //添加到集合中
        add(object);
        //传递给我们的回调,让我们自己配置
        configureAction.execute(object);
        return object;
    }

}

可以看到实现了NamedDomainObjectContainer的3个create方法,最主要的是第三个方法,我们再次回顾一下build.gradle里面


    buildTypes {

        create("test", object : Action<ApplicationBuildType> {

            override fun execute(t: ApplicationBuildType) {
                t.isMinifyEnabled = false
                t.isDebuggable = true
            }
        })
    }

对比一下上面的方法,是不是觉得柳暗花明了😎😎😎

我们知道,create创建的就是BuildType实例(它实现了ApplicationBuildType),这里主要做了3件事:

  • 创建BuildType实例
  • 添加到缓存集合中
  • 传递给我们的回调,由我们自己去配置

看到希望了,我们接着往下看,我们的BuildType是怎么创建出来的,doCreate是个抽象方法,FactoryNamedDomainObjectContainer:


    @Override
    protected T doCreate(String name) {
        return factory.create(name);
    }

看到这个方法,我震惊了🙃🙃🙃我费尽心思追了这么久想看factory是什么终于找到了FactoryNamedDomainObjectContainer,你告诉我创建BuildType的还不是它?🤦‍♂️可是我能有什么办法啊,为了维护世界和平,为了沉没成本,只能继续看啊!

那我们看factory是哪里来的,记得我说前面特意贴的构造函数吗,竟然是构造函数传进来的,那我就返回往前看,发现我们来时的路上每一个方法的参数都是它的身影,直到LegacyVariantInputManager

    override val buildTypeContainer: NamedDomainObjectContainer<BuildType> =
        dslServices.domainObjectContainer(
            BuildType::class.java, BuildTypeFactory(dslServices)
        )

看它的第二个参数点进去:


public class BuildTypeFactory implements NamedDomainObjectFactory<BuildType> {

    @NonNull private DslServices dslServices;

    public BuildTypeFactory(@NonNull DslServices dslServices) {
        this.dslServices = dslServices;
    }

    @NonNull
    @Override
    public BuildType create(@NonNull String name) {
        return dslServices.newInstance(BuildType.class, name, dslServices);
    }
}


诺,就是这里了,后面我实在不想浪费时间继续贴了,相信大家已经看腻了,总结六个字——反射创建实例


流程总结

ok,到这一步,我们就明白了:

  • BuildType是由FactoryNamedDomainObjectContainer创建的;
  • 实例是由BuildTypeFactory反射创建的;
  • BuildType实例创建后添加到了FactoryNamedDomainObjectContainer,然后又给到我们的回调由我们自己配置

AppPlugin创建默认BuildType和我们自己在build.gradle 添加方式是一样的。

5. 回归初心——扩展是如何传递数据给插件的呢?

我们回溯上篇章的内容,找到各个类的从属关系:AppExtension持有ApplicationVariantFactory,后者在前者实例化时通过构造函数注入,它又持有DslServiceImpl… :

  • AppExtension -> ApplicationVariantFactory ->DslServiceImpl->FactoryNamedDomainObjectContainer->BuildType,
  • 而 ApplicationVariantFactory 和 DslServiceImpl 是BasePlugin的成员变量

简单的结构图:

在这里插入图片描述

总结

所以我们可以直观的理解为,ApplicationVariantFactory 就是注入A品牌Extension的一个callback,build.gralde里面看似调用AppExtension的函数,实则调用AppPlugin的成员。

经过千难万险,坎坷曲折,一路不停的追踪代码,最终我们得出这样一个很简单额结论,是不是很开心呢😂

6 应用

好,既然我们已经明白了这个原理,那么我们就可以在我们插件中应用。

改造我们的扩展:


public class RocketExtensions {

    //多平台支持。如若支持,会默认配置多个flavor
    private RocketConfigs mExtensionContainer;


    public RocketExtensions(BoosterConfigs container) {
        mExtensionContainer = container;
    }

    public void setMultiFlavor(boolean multi) {
        mExtensionContainer.setMultiFlavor(multi);
    }
}


新增一个配置类RocketConfigs:


public class RocketConfigs {

    public boolean isMultiFlavor;

    public void setMultiFlavor(boolean multi) {
        System.out.println("setMultiFlavor:" + multi);
        this.isMultiFlavor = multi;
        //doSth
    }
}

插件类:


public class BoosterPlugin implements Plugin {

    @Override
    public void apply(Project project) {
        super.apply(project);

        //初始化配置类
        RocketConfigs container = new BoosterConfigs();
        //注入扩展
        project.getExtensions().create("rocket", RocketExtensions.class, container);
    }
}

然后发布,在应用的build.gradle里使用:


plugins {
    id("com.android.application")
    id("gradle-rocket")
}


rocket {
    setMultiFlavor(true)
}
android{
	//...
}

同步一下就可以看到build输出

在这里插入图片描述


为了看明白这块儿代码真的费尽心思,弯弯绕绕,脑袋都晕了,不过最终搞懂这些东西还是非常开心的。

好了,本篇到此结束

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是在Spring Boot中使用Gradle创建名为`app-common`的项目的步骤: 1. 打开终端(或命令行工具),进入您想要创建项目的文件夹。 2. 运行以下命令创建一个新的Gradle项目: ``` gradle init --type java-library ``` 3. 接下来,您需要回答一些问题以配置项目。根据您的需要回答即可。例如,您可以按Enter键使用默认选项: ``` Select type of project to generate: 1: basic 2: application 3: library 4: Gradle plugin Enter selection (default: basic) [1..4] 3 Select implementation language: 1: C++ 2: Groovy 3: Java 4: Kotlin 5: Swift Enter selection (default: Java) [1..5] 3 Select build script DSL: 1: Groovy 2: Kotlin Enter selection (default: Groovy) [1..2] 1 Select test framework: 1: JUnit 4 2: JUnit Jupiter 3: TestNG Enter selection (default: JUnit 4) [1..3] 1 Project name (default: app-common): Source package (default: app.common): ``` 4. 进入`app-common`文件夹并打开build.gradle文件。将以下内容添加到文件中: ``` plugins { id 'org.springframework.boot' version '2.5.5' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' java.sourceCompatibility = '11' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' } ``` 这段代码添加了Spring Boot、MyBatis和Web依赖项,您可以根据需要添加其他依赖项。 5. 创建一个名为`AppCommonApplication.java`的Java类,并将以下内容添加到文件中: ``` package app.common; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class AppCommonApplication { public static void main(String[] args) { SpringApplication.run(AppCommonApplication.class, args); } } ``` 这个类是Spring Boot应用程序的入口点。 6. 创建一个名为`application.properties`的文件,并将以下内容添加到文件中: ``` spring.application.name=app-common server.port=8080 ``` 这个文件配置了应用程序的名称和端口号。您可以根据需要更改这些值。 7. 创建一个名为`logback.xml`的文件,并将以下内容添加到文件中: ``` <configuration> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="org.springframework" level="INFO" additivity="false"> <appender-ref ref="console" /> </logger> <root level="INFO"> <appender-ref ref="console" /> </root> </configuration> ``` 这个文件配置了日志记录器。您可以根据需要更改这些值。 现在,您已经成功创建了一个名为`app-common`的Spring Boot项目,并使用Gradle进行构建。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值