如果要实现一个自定义的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,比如:
#定义自动装配的类=>RedissonCofiguration
org.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 @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
从源码上看,只有在classpath下能找到你需要的conditionOnClass类才会构建这个bean。比如你想写一个redisson的自动装配:
/**
* redisson配置:配置、自动配置、配置条件
*/
@Configuration
@EnableConfigurationProperties(value = RedissonProperties.class)
@ConditionalOnClass(RedissonProperties.class)
public class RedissonConfiguration {
}
使用@ConditionOnMissingBean:
@ConditionalOnMissingBean,它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常,以此来告诉开发人员。
通常的相关注解:
@ConditionalOnBean // 当给定的bean存在时,则实例化当前Bean
@ConditionalOnMissingBean // 当给定的bean不存在时,则实例化当前Bean
@ConditionalOnClass // 当给定的类名在类路径上存在,则实例化当前Bean
@ConditionalOnMissingClass // 当给定的类名在类路径上不存在,则实例化当前Bean
创建RessionClient的方式:基于对应的方式创建客户端,因为Ression有很多模式,哨兵、主从、单例、集群、云托管模式,拿到对应的模式的配置后,创建对应的客户端bean:
@Bean
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redissonClient() {
// 创建Redisson客户端对象对象
return Redisson.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 @interface SpringBootApplication {
// 省略代码,主要包含的方法:排掉特定自动注入的配置,通过名称或者类方式,进行基包扫描、或者classes、代理bean方法等
}
同时我们可以看到SpringBootApplication里面的所有方法,都使用了一个注解@AliasFor。那这个组件有什么用呢?这个注解用于桥接到其它注解,该注解的属性中指定的所桥接的注解类,减少用户使用多注解带来的麻烦。
其关键就在@EnableAutoConfiguration这个注解中,这个注解里面有一个@Import注解,里面这个类自动配置导入选择器类:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
如果我们想使用xml进行配置的话,此时在springboot启动的时候可以使用@Import将配置进行导入,实现配置注入的目的。而AutoConfigurationImportSelector实现了DeferredImportSelector延迟导入选择器,也即ImportSelector的子类。那ImportSelector里面有什么方法呢?里面有两个方法一个是选择导入的方法、一个是排掉过滤的方法,下面可以看到选择导入方法的入参是导入类元数据。
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
那这个选择导入的过程又是怎样的呢?
SpringFactoriesLoader加载器加载指定ClassLoader下面的所有的META-INF/spring.factories文件,并将文件解析内容存在Map<string,list>中。然后通过loadFactoryNames传递过来的class的名称从map中获取该类的配置列表。通过Set集合进行去重操作。执行过滤组件操作,而这些操作都是在AutoConfigurationImportFilter接口下的组件实现的,也即FilterSpringBootCondition实现抽象类的。下面有OnBeanCondition、OnClassCondition、OnWebApplicationCondition的getOutcomes方法。</string,list
执行fire操作,fireAutoConfigurationImportEvents,此时会执行事件注册。
而其重要的方法就是getAutoConfigurationEntry就是自动装配的重点。