如何实现一个自定义的starter

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: SpringFactoriesLoader加载器加载指定ClassLoader下面的所有的META-INF/spring.factories文件,并将文件解析内容存在Map<string,list>中。然后通过loadFactoryNames传递过来的class的名称从map中获取该类的配置列表。通过Set集合进行去重操作。执行过滤组件操作,而这些操作都是在AutoConfigurationImportFilter接口下的组件实现的,也即FilterSpringBootCondition实现抽象类的。下面有OnBeanCondition、OnClassCondition、OnWebApplic

如果要实现一个自定义的starter,首先需要引入两个依赖spring-boot的jar包:spring-boot-autoconfigure和spring-boot-configuration-processor:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>

在resource中新建META-INF文件夹,创建spring.factories,比如:

#定义自动装配的类=>RedissonCofigurationorg.springframework.boot.autoconfigure.EnableAutoConfiguration=com.study.configuration.RedissonConfiguration

同时你的starter的名称:

<groupId>com.study</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>1.0.0</version>

同时需要基于存在某种条件才进行装配时,可以使用@ContionOnClass.

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented@Conditional(OnClassCondition.class)
public@interfaceConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}

从源码上看,只有在classpath下能找到你需要的conditionOnClass类才会构建这个bean。比如你想写一个redisson的自动装配:

/*** redisson配置:配置、自动配置、配置条件*/@Configuration@EnableConfigurationProperties(value=RedissonProperties.class)
@ConditionalOnClass(RedissonProperties.class)
publicclassRedissonConfiguration {
}

使用@ConditionOnMissingBean:

@ConditionalOnMissingBean,它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常,以此来告诉开发人员。

通常的相关注解:

@ConditionalOnBean// 当给定的bean存在时,则实例化当前Bean@ConditionalOnMissingBean// 当给定的bean不存在时,则实例化当前Bean@ConditionalOnClass// 当给定的类名在类路径上存在,则实例化当前Bean@ConditionalOnMissingClass// 当给定的类名在类路径上不存在,则实例化当前Bean

创建RessionClient的方式:基于对应的方式创建客户端,因为Ression有很多模式,哨兵、主从、单例、集群、云托管模式,拿到对应的模式的配置后,创建对应的客户端bean:

@Bean@ConditionalOnMissingBean(RedissonClient.class)
publicRedissonClientredissonClient() {
// 创建Redisson客户端对象对象returnRedisson.create(config);
}

同时还需要一些特定的信息:

相关bean:lockAop分布式锁、MQAop发送AOP、RedissonBinary操作对象二进制、RedissonObject操作对象、RedissonCollection操作集合、RedissonClient重要,此时就可以基于@ConditionOnMissBean的方式进行创建,从而实现自定装配。此时可以基于分布式锁Aop切面来做拦截,对分布式锁进行增强操作,也即对当前拿到的锁信息进行判断。对锁的模式进行判断,如果当前的锁模式为自动的,则此时根据你所的key进行判断,如果keys的长度>1,则使用红锁,否者使用可重入式锁。如果锁模式不是联锁&&红锁&&长度大于1,此时会抛异常

如果是公平锁,则直接处理,如果是红锁,则需要变量keys,对锁进行添加到RLock中,对锁进行遍历,添加到数组中,然后将其重新赋值给红锁,否者放入到可重入式锁,或者读锁或者写锁中。执行aop.

那可自动装配又是怎样实现的呢?其关键在于@SpringBootApplication这个注解上,这个组件是一个组合注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented@Inherited@SpringBootConfiguration//springboot配置注解@EnableAutoConfiguration//可以自动注入配置@ComponentScan(excludeFilters= { @Filter(type=FilterType.CUSTOM, classes=TypeExcludeFilter.class),
@Filter(type=FilterType.CUSTOM, classes=AutoConfigurationExcludeFilter.class) }) //进行组件扫描,同时排掉过滤信息public@interfaceSpringBootApplication {
// 省略代码,主要包含的方法:排掉特定自动注入的配置,通过名称或者类方式,进行基包扫描、或者classes、代理bean方法等}

同时我们可以看到SpringBootApplication里面的所有方法,都使用了一个注解@AliasFor。那这个组件有什么用呢?这个注解用于桥接到其它注解,该注解的属性中指定的所桥接的注解类,减少用户使用多注解带来的麻烦。

其关键就在@EnableAutoConfiguration这个注解中,这个注解里面有一个@Import注解,里面这个类自动配置导入选择器类:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)
public@interfaceEnableAutoConfiguration {
StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}

如果我们想使用xml进行配置的话,此时在springboot启动的时候可以使用@Import将配置进行导入,实现配置注入的目的。而AutoConfigurationImportSelector实现了DeferredImportSelector延迟导入选择器,也即ImportSelector的子类。那ImportSelector里面有什么方法呢?里面有两个方法一个是选择导入的方法、一个是排掉过滤的方法,下面可以看到选择导入方法的入参是导入类元数据。

publicinterfaceImportSelector {
String[] selectImports(AnnotationMetadataimportingClassMetadata);
}

那这个选择导入的过程又是怎样的呢?

微信图片_20221214032240.png


SpringFactoriesLoader加载器加载指定ClassLoader下面的所有的META-INF/spring.factories文件,并将文件解析内容存在Map中。然后通过loadFactoryNames传递过来的class的名称从map中获取该类的配置列表。通过Set集合进行去重操作。执行过滤组件操作,而这些操作都是在AutoConfigurationImportFilter接口下的组件实现的,也即FilterSpringBootCondition实现抽象类的。下面有OnBeanCondition、OnClassCondition、OnWebApplicationCondition的getOutcomes方法。

执行fire操作,fireAutoConfigurationImportEvents,此时会执行事件注册。

而其重要的方法就是getAutoConfigurationEntry就是自动装配的重点。



相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
JSON Java 应用服务中间件
Spring Boot运行原理及功能实现方式
Spring Boot运行原理及功能实现方式
396 0
Spring Boot运行原理及功能实现方式
|
4月前
|
Java Maven Spring
Springboot-starter的自动配置原理-及案例实现4
Springboot-starter的自动配置原理-及案例实现4
|
4月前
|
Java Spring
Springboot-starter的自动配置原理-及案例实现
Springboot-starter的自动配置原理-及案例实现
|
4月前
|
Java Spring 容器
Springboot-starter的自动配置原理-及案例实现5
Springboot-starter的自动配置原理-及案例实现5
Springboot-starter的自动配置原理-及案例实现2
Springboot-starter的自动配置原理-及案例实现2
|
4月前
|
Java Maven
Springboot-starter的自动配置原理-及案例实现7
Springboot-starter的自动配置原理-及案例实现7
Springboot-starter的自动配置原理-及案例实现6
Springboot-starter的自动配置原理-及案例实现6
Springboot-starter的自动配置原理-及案例实现3
Springboot-starter的自动配置原理-及案例实现3
|
9月前
|
开发框架 SpringCloudAlibaba Java
Spring Boot自动配置原理详解和自定义封装实现starter
我们一直在强调`Spring Boot`能成为当下主流首选开发框架的主要原因在于其核心思想:**约定大于配置,自动配置,条件装配**。基于这些特性使得`Spring Boot`集成其他框架非常简单快捷
122 0
 Spring Boot自动配置原理详解和自定义封装实现starter
|
10月前
|
消息中间件 缓存 前端开发
Spring Boot自定义启动器的实现原理和实际应用场景?
Spring Boot自定义启动器的实现原理和实际应用场景?
130 0