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

本文主要是介绍Gradle系列(3)——Gradle extension(gradle扩展),如何自定义gradle扩展,AppPlugin,AppExtension原理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1.什么是Extensions
    • 2.如何自定义Extension
    • 3.问题来了——如何通过自定义Extension给Plugin传递数据
    • 4.BaseAppModuleExtension和AppPlugin部分原理
      • BuildTypes是如何创建并传递数据给AppPlugin的?
      • AppPlugin是如何接收数据的?
      • buildTypeContainer
      • 流程总结
    • 5. 回归初心——扩展是如何传递数据给插件的呢?
    • 6 应用

系列: 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>  {@Overridepublic 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>  {@Overridepublic void apply(Project project) {super.apply(project);TestExtension anyTest = project.getExtensions().create("anyTest", TestExtension.class);project.afterEvaluate(new Action<Project>() {@Overridepublic 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>() {@Overridepublic 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 = falseisDebuggable = truesigningConfigs { }}//。。。
}

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

buildTypes {create("test", object : Action<ApplicationBuildType> {override fun execute(t: ApplicationBuildType) {t.isMinifyEnabled = falset.isDebuggable = true}})}

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

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

在这里插入图片描述

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

AppPlugin是如何接收数据的?

查看AppPlugin源码


public class AppPluginextends AbstractAppPlugin<com.android.build.api.dsl.ApplicationExtension,ApplicationAndroidComponentsExtension,ApplicationVariantBuilderImpl,ApplicationVariantImpl> {@Injectpublic AppPlugin(ToolingModelBuilderRegistry registry,SoftwareComponentFactory componentFactory,BuildEventsListenerRegistry listenerRegistry) {super(registry, componentFactory, listenerRegistry);}@NonNull@Overrideprotected BaseExtension createExtension(@NonNull DslServices dslServices,@NonNull GlobalScope globalScope,@NonNullDslContainerProvider<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方法

@Overridepublic 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@Overrideprotected 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 buildTypedslContainers.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,查看它的方法:

@Overridepublic <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 {@Overridepublic <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;}@Overrideprotected T doCreate(String name) {return factory.create(name);}
}

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

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

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


public abstract class AbstractNamedDomainObjectContainer<T> extends DefaultNamedDomainObjectSet<T> implements NamedDomainObjectContainer<T>, HasPublicType {@Overridepublic T create(String name) {assertMutable("create(String)");return create(name, Actions.doNothing());}@Overridepublic T create(String name, Closure configureClosure) {assertMutable("create(String, Closure)");return create(name, ConfigureUtil.configureUsing(configureClosure));}@Overridepublic 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 = falset.isDebuggable = true}})}

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

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

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

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

@Overrideprotected 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@Overridepublic 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 {//多平台支持。如若支持,会默认配置多个flavorprivate 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 {@Overridepublic 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输出

在这里插入图片描述


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

好了,本篇到此结束

这篇关于Gradle系列(3)——Gradle extension(gradle扩展),如何自定义gradle扩展,AppPlugin,AppExtension原理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/912267

相关文章

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

PostgreSQL的扩展dict_int应用案例解析

《PostgreSQL的扩展dict_int应用案例解析》dict_int扩展为PostgreSQL提供了专业的整数文本处理能力,特别适合需要精确处理数字内容的搜索场景,本文给大家介绍PostgreS... 目录PostgreSQL的扩展dict_int一、扩展概述二、核心功能三、安装与启用四、字典配置方法

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

一文详解Java Stream的sorted自定义排序

《一文详解JavaStream的sorted自定义排序》Javastream中的sorted方法是用于对流中的元素进行排序的方法,它可以接受一个comparator参数,用于指定排序规则,sorte... 目录一、sorted 操作的基础原理二、自定义排序的实现方式1. Comparator 接口的 Lam

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Spring @Scheduled注解及工作原理

《Spring@Scheduled注解及工作原理》Spring的@Scheduled注解用于标记定时任务,无需额外库,需配置@EnableScheduling,设置fixedRate、fixedDe... 目录1.@Scheduled注解定义2.配置 @Scheduled2.1 开启定时任务支持2.2 创建

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源